Python не может найти мой модуль

У меня есть проект python (который я запускаю внутри virtualenv) и который имеет следующую структуру:

Project
├───.git
├───venv
└───src
    ├───__init__.py
    ├───mymodules
    │   ├───__init__.py
    │   ├───module1.py
    │   └───module2.py
    └───scripts
        ├───__init__.py
        └───script.py

script.py

import src.mymodules.module1
...

Я запускаю проект с активированным venv и из каталога Project, используя следующую команду:

(venv)$ python src/scripts/script.py

Выполняется script, но выдается следующая ошибка перед выходом:

Traceback (most recent call last):
  File "src/scripts/script.py", line 1, in <module>
    import src.mymodules.module1
ImportError: No module named src.mymodules.module1

Я попытался запустить оболочку python и попытался импортировать модуль оттуда, и он не дал никаких ошибок. У меня есть _ _init__.py в каждом каталоге в src. Является ли python рассматривающим рабочий каталог как src/scripts? Почему это происходит и как я могу сделать src рабочим каталогом, если это дело?

Ответы

Ответ 1

По сути, при непосредственном выполнении script.py он не знает, что он является частью подмодуля src, и не знает, где может быть модуль с именем src. Это имеет место либо в python 2 или 3.

Как вы знаете, Python находит модули на основе содержимого sys.path. Чтобы импортировать любой модуль, он должен либо находиться в каталоге, указанном в sys.path, либо в том же каталоге, что и script, который вы используете.

Когда вы говорите python src/scripts/script.py, sys.path включает Project/src/scripts/ (потому что там, где script.py), но не Project. Поскольку Project не находится в пути, модули в этом каталоге (src) не могут быть импортированы.

Чтобы исправить это:

Я предполагаю, что ваш script.py является точкой входа для вашего модуля src (например, может быть, это основная программа). Если это правда, то вы можете исправить его, переместив script.py на тот же уровень, что и src:

Project
├───.git
├───venv
|───script.py       <--- script.py moves up here
└───src
    ├───__init__.py
    └───mymodules
        ├───__init__.py
        ├───module1.py
        └───module2.py

Таким образом, script.py может свободно импортировать что-либо в src, но ничего в src не может импортировать script.py.

Если это не так, и script.py действительно является частью src, вы можете использовать python -m аргумент для выполните script.py как часть модуля src следующим образом:

$ python -m src.scripts.script

Поскольку вы сказали python, какой модуль вы используете (src), он будет в пути. Таким образом, script.py будет знать, что это подмодуль src, а затем сможет импортировать из src.

Будьте осторожны в этой ситуации - возможно создать круговой импорт, если что-то в src импортирует src.scripts.script.


В качестве альтернативы обеим этим подходам вы можете изменить sys.path непосредственно в script.py:

import sys
sys.path.insert(0, '/path/to/Project') # location of src 

Пока это работает, это обычно не мое предпочтение. Это требует script.py, чтобы точно знать, как выкладывается ваш код, и может вызвать замедление импорта, если другая программа python пытается импортировать script.py.

Ответ 2

Project
├───.git
├───venv
└───src
    ├───__init__.py
    ├───mymodules
    │   ├───__init__.py
    │   ├───module1.py
    │   └───module2.py
    └───scripts
        ├───__init__.py
        └───script.py

В качестве альтернативы вы можете импортировать, как показано ниже, в script.py

import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__),'../../'))
import src.mymodules.module1

Теперь вы можете запустить script.py файл из любого места.

e.g :
python script.py
python /path to folder/script.py

Ответ 3

Другим решением является создание имени файла "xxx (любое имя).pth" и запись директории вашего проекта (материнской директории вашего src). Поместите этот файл в /virtual_env/lib/pythonXX/site-packages/. Таким образом, вам не нужно импортировать sys.path в ваш скрипт.

Ответ 4

Если вы столкнулись с этой проблемой при работе с Pytest или coverage. Добавление файла __init__.py решит большинство случаев.