252 votes

Importations relatives - ModuleNotFoundError : Aucun module nommé x

C'est la première fois que je m'assois et que je m'essaie à Python 3, et je semble échouer lamentablement. J'ai les deux fichiers suivants :

  1. test.py
  2. config.py

config.py contient quelques fonctions définies ainsi que quelques variables. Je l'ai réduit à ce qui suit :

config.py

debug = True

test.py

import config
print (config.debug)

J'ai aussi un __init__.py

Cependant, j'obtiens l'erreur suivante :

ModuleNotFoundError: No module named 'config'

Je suis conscient que la convention py3 est d'utiliser des importations absolues :

from . import config

Cependant, cela conduit à l'erreur suivante :

ImportError: cannot import name 'config'

Je ne sais donc pas quoi faire... Toute aide est la bienvenue :)

1 votes

Je ne peux pas reproduire l'erreur, comment exécutez-vous ce code ?

0 votes

J'exécute test.py via pyCharm avec Python 3.6. Le vôtre s'exécute-t-il correctement ?

2 votes

Je l'exécute avec idle qui vient avec python, et aussi comme python test.py et cela fonctionne parfaitement bien. Je n'ai pas pyCharm, mais peut-être est-ce une mauvaise configuration de pyCharm qui cause le problème.

227voto

Igonato Points 4979

TL;DR : Vous ne pouvez pas faire d'importations relatives à partir du fichier que vous exécuter depuis __main__ Le module ne fait pas partie d'un paquet.

Importations absolues - importer quelque chose de disponible sur sys.path

Importations relatives - Importer quelque chose de relatif au module actuel, doit faire partie d'un paquet.

Si vous exécutez les deux variantes exactement de la même manière, l'une d'elles devrait fonctionner. Voici un exemple qui devrait vous aider à comprendre ce qui se passe. Ajoutons une autre main.py avec la structure globale du répertoire comme ceci :

.
./main.py
./ryan/__init__.py
./ryan/config.py
./ryan/test.py

Et mettons à jour test.py pour voir ce qui se passe :

# config.py
debug = True

# test.py
print(__name__)

try:
    # Trying to find module in the parent package
    from . import config
    print(config.debug)
    del config
except ImportError:
    print('Relative import failed')

try:
    # Trying to find module on sys.path
    import config
    print(config.debug)
except ModuleNotFoundError:
    print('Absolute import failed')

# main.py
import ryan.test

Allons-y. test.py d'abord :

$ python ryan/test.py
__main__
Relative import failed
True

Ici "test" est le site __main__ et ne sait rien de l'appartenance à un paquet. Cependant, import config devrait fonctionner, puisque le ryan sera ajouté à sys.path .

Allons-y. main.py à la place :

$ python main.py
ryan.test
True
Absolute import failed

Et ici, test est à l'intérieur du paquet "ryan" et peut effectuer des importations relatives. import config échoue car les importations relatives implicites ne sont pas autorisées dans Python 3.

J'espère que cela vous a aidé.

P.S. : Si vous vous en tenez à Python 3, vous n'avez plus besoin de __init__.py des fichiers.

4 votes

Y a-t-il quelque chose que je puisse faire pour que les importations absolues fonctionnent toujours ? Par exemple, appeler sys.path.append('/some/path/my_module') à l'intérieur de /some/path/my_module/__init__.py ?

5 votes

@JamesT. Oui, il est assez courant de modifier sys.path pendant l'exécution ( github.com/ ). Vous pouvez également définir la variable d'environnement PYTHONPATH.

9 votes

"si vous vous en tenez à Python 3, il n'y a plus besoin de __init__.py fichiers." Intéressant. Pouvez-vous développer ce point ? J'avais l'impression que le mécanisme de résolution des paquets n'avait pas tellement changé entre 2 et 3.

79voto

J'ai trouvé la solution. Très frustrant, surtout venant de python2.

Vous devez ajouter un . au module, qu'il soit relatif ou absolu.

J'ai créé la configuration du répertoire comme suit.

/main.py
--/lib
  --/__init__.py
  --/mody.py
  --/modx.py

modx.py

def does_something():
    return "I gave you this string."

mody.py

from modx import does_something

def loaded():
    string = does_something()
    print(string)

main.py

from lib import mody

mody.loaded()

lorsque j'exécute main, voici ce qui se passe

$ python main.py
Traceback (most recent call last):
  File "main.py", line 2, in <module>
    from lib import mody
  File "/mnt/c/Users/Austin/Dropbox/Source/Python/virtualenviron/mock/package/lib/mody.py", line 1, in <module>
    from modx import does_something
ImportError: No module named 'modx'

J'ai lancé 2to3, et la sortie principale était la suivante .

RefactoringTool: Refactored lib/mody.py
--- lib/mody.py (original)
+++ lib/mody.py (refactored)
@@ -1,4 +1,4 @@
-from modx import does_something
+from .modx import does_something

 def loaded():
     string = does_something()
RefactoringTool: Files that need to be modified:
RefactoringTool: lib/modx.py
RefactoringTool: lib/mody.py

J'ai dû modifier l'instruction d'importation de mody.py pour le corriger.

try:
    from modx import does_something
except ImportError:
    from .modx import does_something

def loaded():
    string = does_something()
    print(string)

Puis j'ai relancé main.py et j'ai obtenu le résultat attendu

$ python main.py
I gave you this string.

Enfin, juste pour le nettoyer et le rendre portable entre 2 et 3.

from __future__ import absolute_import
from .modx import does_something

63voto

Vous devez ajouter le chemin d'accès à votre projet dans le champ PYTHONPATH et assurez-vous d'utiliser importations absolues .


Pour UNIX (Linux, OSX, ...)

export PYTHONPATH="${PYTHONPATH}:/path/to/your/project/"

Pour Windows

set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\project\

Importations absolues

Supposons que nous ayons la structure de projet suivante,

└── myproject
    ├── mypackage
    │   ├── a.py
    └── anotherpackage
        ├── b.py
        ├── c.py
        └── mysubpackage
            └── d.py

Assurez-vous simplement de référencer chaque importation à partir du répertoire racine du projet. Par exemple,

# in module a.py
import anotherpackage.mysubpackage.d

# in module b
import anotherpackage.c
import mypackage.a

Pour une explication plus complète, veuillez vous référer à l'article Comment corriger ModuleNotFoundError et ImportError ?

49voto

Santosh Pillai Points 2584

La définition de PYTHONPATH peut également aider à résoudre ce problème.

Voici comment procéder sous Windows

set PYTHONPATH=.

12 votes

En plaçant PYTHONPATH dans le répertoire principal du code, j'ai résolu le problème !

3 votes

Fonctionne également sous Linux. export PYTHONPATH=.

17voto

Vinod Rane Points 21

Vous pouvez simplement ajouter le fichier suivant dans votre répertoire de tests, et python l'exécutera avant les tests.

__init__.py file

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X