Импорт Python с вложенными модулями и тестами + лучшие практики для структуры приложения - PullRequest
0 голосов
/ 06 ноября 2018

Резюме

У меня следующая структура каталогов. Моя цель - сделать module1 импортируемым (в данном случае из test_module1.py), а также иметь возможность вызывать module1.py напрямую, когда я нахожусь в каталоге module1. Я чувствую, что или есть хорошее решение этой проблемы, или что-то не так с тем, как я структурирую приложение, поэтому предлагаемые решения или изменения в структуре приложения могут решить эту проблему

Детали

Структура каталогов

.
├── module1
│   ├── __init__.py
│   ├── module1.py
│   ├── module2
│   │   ├── __init__.py
│   │   └── module2.py
└── tests
    └── test_module1.py

Мои файлы заполняются следующим образом

test_module1.py

from module1.module1 import Module1
def test_fake():
    pass

module1.py

from module2.module2 import Module2
class Module1:
    pass #or regular __init__, etc

module2.py

class Module2:
    pass #or regular __init__, etc

Так что я бы хотел иметь возможность

1) запустить module1 из каталога module1

cd module1
python module1.py

2) запустить тесты из основного каталога, используя

pytest

В вышеприведенном случае # 1 работает, но # 2 не работает с

    from module1.module1 import Module1
E   ModuleNotFoundError: No module named 'module1'

Это связано с проверкой pytest в тестовой директории только для модулей. Это решается запуском python -m pytest, что позволяет искать в каталоге .

Тем не менее, все еще есть ошибка, и это то, что я не могу понять.

Traceback:
tests/test_module1.py:1: in <module>
    from module1.module1 import Module1
module1/module1.py:1: in <module>
    from module2.module2 import Module2
E   ModuleNotFoundError: No module named 'module2.Module2'

Проверенные решения

Тест находит модуль 1, но при импорте модуля 1 ему нужно найти модуль 2. Он ищет это в каталоге ., но не в ./module1. Эту проблему можно решить, изменив импорт в module1.py с from module2.module2 import Module2 на from .module2.module2 import Module2 (обратите внимание на добавленный . в начале). Это относительный импорт, абсолютный импорт также решает проблему. Оба решения заставляют python -m pytest проходить, так что # 2 работает. Тем не менее, оба также нарушают функциональность # 1, где я хотел иметь возможность запускать module1 непосредственно из модуля. Пройдя немного дальше, я попытался запустить module1.py из ., это выдает ошибку: No module named '__main__.module2'; '__main__' is not a package. Я смог наконец заставить его работать, используя python -m module1.module1, но это кажется супер каруселью. Подробнее об этих решениях ниже

ModuleNotFoundError: Что значит __main__ не является пакетом?

Как сделать относительный импорт в Python?

По сути, я обнаружил, что абсолютный и относительный импорт теперь вынуждают вызывать module1 из корневого каталога с помощью -m. Мне кажется, это указывает на большую проблему, заключающуюся в том, что при использовании вложенных модулей в Python модуль более низкого уровня никогда не сможет использоваться сам по себе. Скажем, module2 содержит два файла, и один нужно импортировать другой. Он также будет вынужден использовать относительный или абсолютный импорт, и теперь module2 должен запускаться из module1 с использованием -m, что затрудняет создание одного из этих сценариев.

Я чувствую, что, возможно, я тоже что-то делаю в файлах __init__.py и нашел предложение сделать относительный импорт в __init__.py в документации по Python https://docs.python.org/3/reference/import.html#submodules, но у меня это не сработало .

Некоторая справка (необязательно читать) : Причины, по которым я хочу это:

1), который я разрабатываю в возвышенном тексте, чтобы я мог просто создать файл непосредственно при кодировании

2) Я потратил много времени на поиск хорошего способа структурирования приложения на Python с точки зрения модулей и тестов, и это то, что я придумал. Если я что-то делаю неправильно, пожалуйста, дайте мне знать, но если это хорошая структура, то возможность запуска кода непосредственно в модуле, а также возможность импорта модуля (из тестового сценария или в другом месте) кажутся довольно простой функциональностью, которая должно быть достижимо.

...