web-dev-qa-db-fra.com

Python: 'ModuleNotFoundError' lors d'une tentative d'importation de module à partir d'un package importé

J'utilise Python 3.7.1 sur macOS Mojave version 10.14.1

Voici ma structure de répertoires:

man/                          
  Mans/                  
          man1.py
  MansTest/
          SoftLib/
                  Soft/
                      SoftWork/
                              manModules.py
          Unittests/
                    man1test.py

man1.py contient les instructions import, que je ne veux pas changer:

from Soft.SoftWork.manModules import *

man1test.py contient les instructions import ​​suivantes:

from ...MansTest.SoftLib import Soft
from ...Mans import man1

J'ai besoin du second import ​​in man1test.py car man1test.py a besoin d'accéder à une fonction dans man1.py.

Mon raisonnement derrière la première importation (Soft) était de faciliter la déclaration import ​​susmentionnée dans man1.py.

Contrairement à mes attentes, cependant, l'instruction import ​​dans man1.py donne lieu à:

ModuleNotFoundError: No module named 'Soft'

quand je cours

python3 -m man.MansTest.Unittests.man1test

à partir d'un répertoire ci-dessus man /.

Existe-t-il un moyen de résoudre cette erreur sans modifier l'instruction import ​​dans man1.py et sans rien ajouter à sys.path?

Éditer: python3 -m man.ManTest.Unittests.man1test de la version originale de la question a été remplacé par python3 -m man.MansTest.Unittests.man1test

3
strangeloop

Il y a quelques source de confusion avec votre configuration mais je vais essayer de vous donner ce que vous voulez.

[~ # ~] d'abord [~ # ~] , si vous voulez pouvoir accéder à man1.py from man1test.py AND manModules.py from man1.py, vous devez configurer correctement vos fichiers comme - packages et modules .

Les packages sont un moyen de structurer l'espace de noms des modules de Python en utilisant des "noms de modules en pointillés". Par exemple, le nom du module A.B désigne un sous-module nommé B dans un package nommé A.

...

Lors de l'importation du package, Python recherche dans les répertoires sur sys.path recherche du sous-répertoire du package.

Le __init__.py les fichiers sont nécessaires pour que Python traite les répertoires comme contenant des packages; cela est fait pour empêcher les répertoires avec un nom commun, comme string, de masquer involontairement des modules valides qui se produire plus tard sur le chemin de recherche du module.

Vous devez le configurer pour quelque chose comme ceci:

man
|- __init__.py
|- Mans
   |- __init__.py
   |- man1.py
|- MansTest
   |- __init.__.py
   |- SoftLib
      |- Soft
         |- __init__.py
         |- SoftWork
            |- __init__.py
            |- manModules.py
      |- Unittests
         |- __init__.py
         |- man1test.py

[~ # ~] seconde [~ # ~] , pour le "ModuleNotFoundError: No module named 'Soft' "erreur provoquée par from ...Mans import man1 in man1test.py, la solution documentée à cela consiste à ajouter man1.py à sys.path puisque Mans est en dehors du package MansTest. Voir Le chemin de recherche du module dans la documentation Python. Mais si vous ne voulez pas modifier sys.path directement, vous pouvez également modifier PYTHONPATH:

sys.path est initialisé à partir de ces emplacements:

  • Le répertoire contenant le script d'entrée (ou le répertoire courant lorsqu'aucun fichier n'est spécifié).
  • PYTHONPATH (une liste de noms de répertoires, avec la même syntaxe que la variable Shell PATH).
  • Valeur par défaut dépendante de l'installation.

[~ # ~] troisième [~ # ~] , pour from ...MansTest.SoftLib import Soft dont vous avez dit " devait faciliter la déclaration d'importation susmentionnée dans man1.py", c'est maintenant ainsi que les importations fonctionnent. Si vous souhaitez importer Soft.SoftLib dans man1.py, vous devez configurer man1.py pour trouver = Soft.SoftLib et importez-le directement.

Cela dit, voici comment je l'ai fait fonctionner.

man1.py:

from Soft.SoftWork.manModules import *
# no change to import statement but need to add Soft to PYTHONPATH

def foo():
    print("called foo in man1.py")
    print("foo call module1 from manModules: " + module1())

man1test.py

# no need for "from ...MansTest.SoftLib import Soft" to facilitate importing..
from ...Mans import man1

man1.foo()

manModules.py

def module1():
    return "module1 in manModules"

Sortie borne:

$ python3 -m man.MansTest.Unittests.man1test
Traceback (most recent call last):
  ...
    from ...Mans import man1
  File "/temp/man/Mans/man1.py", line 2, in <module>
    from Soft.SoftWork.manModules import *
ModuleNotFoundError: No module named 'Soft'
$ PYTHONPATH=$PYTHONPATH:/temp/man/MansTest/SoftLib
$ export PYTHONPATH
$ echo $PYTHONPATH
:/temp/man/MansTest/SoftLib
$ python3 -m man.MansTest.Unittests.man1test
called foo in man1.py
foo called module1 from manModules: module1 in manModules 

À titre de suggestion, peut-être repenser le but de ces fichiers SoftLib. Est-ce une sorte de "pont" entre man1.py et man1test.py? La façon dont vos fichiers sont configurés en ce moment, je ne pense pas que cela fonctionnera comme vous vous attendez. En outre, il est un peu déroutant pour le code en cours de test ( man1.py) d'importer des éléments sous le dossier de test ( MansTest).

8
Gino Mempin