web-dev-qa-db-fra.com

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

J'essaie 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, lorsque 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 de moi, j'ai trouvé " le chemin relatif ne fonctionnait 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?

651
skytreader

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

python -m pkg.tests.core_test
389

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

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

Comme suggéré par Igancio, vous pouvez l'exécuter en utilisant l'option -m. Si une partie de votre package doit être exécutée en tant que script, vous pouvez également utiliser l'attribut __package__ pour indiquer au fichier le nom qu'il est supposé avoir dans la hiérarchie des packages. 

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

580
BrenBarn

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

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

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

Si vous voulez lancez votre UnitTest à partir de la ligne de commande de manière classique, c'est-à-dire:

python tests/core_test.py

Ensuite, comme dans ce cas, 'composants' et 'tests' sont des dossiers frères, vous pouvez importer le module relatif en utilisant la méthode insert ou append du fichier 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 lancer votre script avec l'argument '-m' (notez que dans ce cas, nous parlons d'un paquet, vous ne devez donc pas donner l'extension '.py'), C'est:

python -m pkg.tests.core_test

Dans un tel 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 afin que votre script fonctionne, peu importe comment il s'appelle . Par exemple:

if __== '__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
165
Paolo Rovelli

Dans core_test.py, procédez comme suit:

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

Si votre cas d'utilisation est d'exécuter des tests et que cela semble être le cas, vous pouvez procéder comme suit. Au lieu d'exécuter votre script de test en tant que python core_test.py, utilisez un framework 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. Ceci résout le problème de __name__ étant __main__ qui a été souligné par @BrenBarn. Ensuite, placez un fichier __init__.py vide dans votre répertoire de test, cela fera du répertoire de test une partie de votre paquet. Ensuite, vous pourrez faire

from ..components.core import GameLoopEvents

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

9
deepak

Ma solution rapide consiste à ajouter le répertoire au chemin:

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

Vieux fil. J'ai découvert que l'ajout d'un __all__= ['submodule', ...] au fichier __init__.py , puis l'utilisation du from <CURRENT_MODULE> import * dans la cible fonctionnent correctement.

2
Laurent

Vous pouvez utiliser from pkg.components.core import GameLoopEvents, par exemple, j'utilise pycharm, le texte ci-dessous représente l'image de la structure de mon projet. Je viens d'importer à partir du paquet racine.

 enter image description here

1
Jayhello

Cette approche a fonctionné pour moi et est moins encombrée que certaines solutions:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

Le répertoire parent est dans mon PYTHONPATH et il y a des fichiers __init__.py dans le répertoire parent et dans ce répertoire.

0
Rick Graves

Essaye ça

import components
from components import *
0
Vaishnavi Bala

Si quelqu'un cherche une solution de contournement, je suis tombé sur un. Voici un peu de contexte. Je voulais tester l'une des méthodes que j'ai dans un fichier. Quand je le lance de l'intérieur 

if __== "__main__":

il s'est toujours plaint des importations relatives. J'ai essayé d'appliquer les solutions ci-dessus, mais sans succès, car il y avait beaucoup de fichiers imbriqués, chacun avec plusieurs importations.

Voici ce que j'ai fait. Je viens de créer un programme de lancement, un programme externe permettant d'importer les méthodes nécessaires et de les appeler. Bien que ce ne soit pas une bonne solution, cela fonctionne.

0
HappyWaters

Comme Paolo a dit, nous avons 2 méthodes d’invocation:

1) python -m tests.core_test
2) python tests/core_test.py

Une différence entre eux est la chaîne sys.path [0]. Puisque l’interprétation cherchera sys.path lors de l’import , nous pouvons faire avec tests/core_test.py:

if __== '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

Et après, nous pouvons exécuter core_test.py avec d’autres méthodes:

cd tests
python core_test.py
python -m core_test
...

Remarque: py36 testé uniquement.

0
zhengcao