Правильная структура проекта Python для пакетов - PullRequest
1 голос
/ 03 октября 2019

Я создаю утилиту Python, которую нужно будет совместно использовать внутри пакетов pip, и которая будет использоваться как в виде библиотеки, так и в качестве инструмента командной строки.

У меня проблемы справильное использование пространств имен и где / когда / если поместить файлы __init__.py для получения согласованной кодовой базы.

  • Я могу заставить работать "библиотеку", поэтому я могу from example_service.example import Example.
  • Я могу заставить работать CLI, поэтому я могу выполнить python example_service/example_cli.py.
  • Я могу заставить работать pytest.
  • Я НЕ МОГУ заставить все три работать одновременно.

Итак

  • Если мой пакет использует from filename import Class или from .filename или from example_service.filename?
  • Должен ли я поместить __init__.py в каталог пакета илине? Кажется, это не нужно для Python3, но я получаю смешанные результаты.

  • Если
    • example.py использует from componenta import ComponentA и
    • example_cli.py использует from example import Example
    • , тогда вызов CLI работает
    • сбой импорта библиотеки с no component named 'componenta'
  • Если
    • example.py использует from example_service.componenta import ComponentA и
    • example_cli.py использует from example import Example
    • , то вызов CLI завершится неудачно с no component named 'example_service'
    • импорт библиотеки работает

В настоящее время у меня есть каталог проекта, который выглядит что-токак это:

project_root/
    example_service/
        example.py
        example_cli.py
        componenta.py
        componentb.py
        tests/
            __init__.py
            test_example.py
    setup.py
    requirements.txt

Внутри моего основного кода lib, example.py, у меня есть:

from componenta import ComponentA

class Example(object):
    def foo(self):
        a = ComponentA()
        print("Example.foo()")

Моя оболочка CLI будет обрабатывать аргументы и прочее, но пока это просто:

#!/usr/bin/env python
from example import Example

class ExampleCli(object):
    def __init__(self):
        print("ExampleCli.init()")
        e = Example()
        e.foo()

if __name__ == '__main__':
    ExampleCli()

И componenta.py - это:

class ComponentA(object):
    def bar(self):
      print("ComponentA.bar()")

(полный код был отправлен на GitHub )

1 Ответ

1 голос
/ 05 октября 2019

Пара моментов:

  • Это правда, что пакет без __init__.py должен работать (начиная с Python 3.3, я думаю). Вы можете оставить это, если вам это не нужно (то есть: вам нечего в нем написать).

  • Я бы не стал звонить python example_service/example_cli.py напрямую, никогда. Для этого у вас есть setuptools точка входа , поэтому в вашем случае вместо этого вызовите example напрямую. (Вы можете удалить shebang и исполняемый бит из example_service/example_cli.py.)

  • из вашего пакета example_service, вы можете использовать from example_service.componenta import ComponentA или from .componenta import ComponentA. Это должно работать одинаково в обоих случаях.

...