Question Comment réparer "Tentative d'importation relative dans un non-paquet" même avec __init__.py


J'essaye de suivre PEP 328, avec la structure de répertoire suivante:

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

Dans core_test.py J'ai la déclaration d'importation suivante

from ..components.core import GameLoopEvents

Cependant, quand je cours, j'obtiens l'erreur suivante:

tests$ python core_test.py 
Traceback (most recent call last):
  File "core_test.py", line 3, in <module>
    from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package

En cherchant autour j'ai trouvé "chemin relatif ne fonctionne pas même avec __init__.py" et "Importer un module à partir d'un chemin relatif"mais ils n'ont pas aidé.

Y a-t-il quelque chose qui me manque ici?


585
2017-07-18 07:59


origine


Réponses:


Oui. Vous ne l'utilisez pas comme un paquet.

python -m pkg.tests.core_test

363
2017-07-18 08:01



Pour élaborer sur Ignacio Vazquez-Abrams répondre:

Le mécanisme d'importation Python fonctionne par rapport à __name__ du fichier actuel. Lorsque vous exécutez un fichier directement, il n'a pas son nom habituel, mais il a "__main__" comme son nom à la place. Les importations relatives ne fonctionnent donc pas.

Vous pouvez, comme l'a suggéré Igancio, l'exécuter en utilisant -m option. Si une partie de votre package doit être exécutée en tant que script, vous pouvez également utiliser le __package__ Attribut pour dire à ce fichier quel nom il est supposé avoir dans la hiérarchie du paquet.

Voir http://www.python.org/dev/peps/pep-0366/ pour plus de détails.


535
2017-07-18 08:26



Vous pouvez utiliser import components.core directement si vous ajoutez le répertoire actuel à sys.path:

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))

189
2017-10-04 21:00



Cela dépend de la façon dont vous voulez lancer votre script.

Si tu veux lancez votre UnitTest depuis la ligne de commande de manière classique, c'est-à-dire:

python tests/core_test.py

Ensuite, puisque dans ce cas 'Composants' et 'tests' sont des dossiers frères, vous pouvez importer le module relatif soit en utilisant le insérer ou la ajouter méthode de sys.path module. Quelque chose comme:

import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents

Sinon, vous pouvez lance ton script avec l'argument '-m' (Notez que dans ce cas, nous parlons d'un paquet, et donc vous ne devez pas donner le '.py' extension), c'est-à-dire:

python -m pkg.tests.core_test

Dans ce cas, vous pouvez simplement utiliser l'importation relative comme vous le faisiez:

from ..components.core import GameLoopEvents

Vous pouvez enfin mélanger les deux approches, de sorte que votre script fonctionnera, peu importe comment il s'appelle. Par exemple:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        from components.core import GameLoopEvents
    else:
        from ..components.core import GameLoopEvents

155
2018-01-10 13:36



Dans core_test.py, procédez comme suit:

import sys
sys.path.append('../components')
from core import GameLoopEvents

11
2018-01-10 17:44



Si votre cas d'utilisation concerne l'exécution de tests, et qu'il semble que ce soit le cas, vous pouvez procéder comme suit. Au lieu d'exécuter votre script de test en tant que python core_test.py utiliser un cadre de test tel que pytest. Ensuite, sur la ligne de commande, vous pouvez entrer

$$ py.test

Cela va exécuter les tests dans votre répertoire. Cela contourne la question de __name__ étant __main__ cela a été souligné par @BrenBarn. Ensuite, mettez un vide __init__.py fichier dans votre répertoire de test, cela fera partie du répertoire de test de votre paquet. Ensuite, vous serez en mesure de faire

from ..components.core import GameLoopEvents

Cependant, si vous exécutez votre script de test en tant que programme principal, les choses échoueront à nouveau. Alors utilisez simplement le coureur d'essai. Peut-être que cela fonctionne aussi avec d'autres coureurs de test tels que nosetests mais je ne l'ai pas vérifié. J'espère que cela t'aides.


8
2017-11-13 17:00



Ma solution est d'ajouter le répertoire au chemin:

import sys
sys.path.insert(0, '../components/')

4
2017-11-01 07:19



Vieux fil. J'ai découvert que l'ajout d'un __all__= ['submodule', ...] au __init__.py fichier, puis en utilisant le from <CURRENT_MODULE> import * dans la cible fonctionne bien.


2
2018-04-18 17:13