Структура проекта Python с относительным импортом и pytest - PullRequest
0 голосов
/ 13 декабря 2018

Представьте себе структуру этого проекта:

myproject
|-- mypkg1
|   |-- __init__.py
|   |-- __main__.py
|   |-- mod1.py
|   |-- mod2.py
|   |-- standalone.py
|
|-- mypkg2
|   |-- __init__.py
|   |-- mod1.py
|   |-- mod2.py
|
|-- tests
|   |-- mypkg1
|   |   | --mod1_test.py
|   |-- mypkg1_mod1_test.py
|
|-- mypkg1_run.py
|-- standalone_run.py
|-- conftest.py
|-- README

С этим содержимым файлов:

mypkg1 .__ init __. Py

import sys
import inspect
from pathlib import Path

sys.path.append(str((Path(inspect.getfile(inspect.currentframe())) / '../..').resolve()))

mypkg1 .__ main __. Py

from .mod1 import mod1_msg
from .mod2 import mod2_msg
from mypkg2 import mod1, mod2

def main():
    print("Hello from mypkg1 main!")
    print(mod1_msg())
    print(mod2_msg())
    print(mod1.mod1_msg())
    print(mod2.mod2_msg())

if __name__ == '__main__':
    main()

mypkg1.mod1.py

def mod1_msg():
    return "Hello from mypkg1.mod1!"

mypkg1.mod2.py

from .mod1 import mod1_msg

def mod2_msg():
    return "Hello from mypkg1.mod2 and {}".format(mod1_msg())

mypkg1.standalone.py

from .mod2 import mod2_msg

def main():
    print("Hello from mypkg1.standalone!")
    print(mod2_msg())

if __name__ == '__main__':
    main()

mypkg2 .__ init __. Py

import sys
import inspect
from pathlib import Path

sys.path.append(str((Path(inspect.getfile(inspect.currentframe())) / '../..').resolve()))

mypkg2.mod1.py

def mod1_msg():
    return "Hello from mypkg2.mod1!"

mypkg2.mod2.py

from .mod1 import mod1_msg

def mod2_msg():
    return "Hello from mypkg2.mod2 and {}".format(mod1_msg())

tests.mypkg1.mod1_test.py и tests.mypkg1_mod1_test.py

from mypkg1.mod1 import mod1_msg

def test_mod1():
    assert mod1_msg() == "Hello from mypkg1.mod1!" 

mypkg1_run.py

from mypkg1.__main__ import main  

if __name__ == '__main__':
    main()

standalone_run.py

from mypkg1.standalone import main

if __name__ == '__main__':
    main()

Общие требования к репозиторию

  1. mypkg1 может использовать скрипты из mypkg2.
  2. Mypkg1 должен запускаться как автономный пакет.
  3. Для тестирования используется Pytest.

Требуемое поведение при использовании

  1. Запустите пакет из директора myrepoy: python -m mypkg1
  2. Запустить пакет вне каталога myrepo: python -m [некоторый путь] .myrepo.mypkg1
  3. Запустить скрипт автономного пакета из каталога myrepo: python -m mypkg1.standalone
  4. Запустите скрипт автономного пакета из внешнего каталога myrepo: python -m [некоторый путь] .myrepo.mypkg1.standalone

Обязательное поведение при тестировании

  1. Вызов pytest из каталога myrepo должен собирать и запускать все тесты из папки тестов
  2. mypkg1_mod1_test.py и mod1_test.py должны запускаться из родительского каталога
  3. mypkg1_mod1_test.py и mod1_test.py должны запускаться из любых внешних каталогов

Выводы

  1. Относительный импорт необходим из-затребуется запуск пакета из пути, находящегося за пределами myrepo.
  2. Эта строка необходима для поиска пакетов одного уровня sys.path.append(str((Path(inspect.getfile(inspect.currentframe())) / '../..').resolve()))

И, наконец, мои Вопросы

  1. Можно ли как-нибудь запустить standalone.py как скрипт из его пакета в этой структуре проекта?Как двойной щелчок по файлу скрипта.Когда я запускаю автономно, как это, я получаю эту ошибку: ModuleNotFoundError: No module named '__main__.mod2'; '__main__' is not a package Эта ошибка вызвана относительным импортом внутри пакета.
  2. Есть ли какой-либо антипаттерн в моем соглашении о структуре проекта / пакета?
  3. Есть ликакие-либо вещи, которые должны быть исправлены или улучшены?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...