Тесты видят исходники, а установленный пакет сломан: зачем нужен src layout

Из корня репозитория import ledger работает, тесты проходят. После установки wheel в другой каталог пакет не находится или теряет подмодуль. В рабочем дереве интерпретатор мог импортировать исходники напрямую, минуя результат сборки. Проверяли один набор файлов, а пользователю доставили другой.

Расположение src/ledger отделяет импортируемый пакет от корня проекта. Чтобы использовать его в окружении разработки, пакет нужно установить, например через python -m pip install -e . . Текущий каталог всё ещё участвует в поиске модулей, но каталога ledger непосредственно в корне уже нет. Это уменьшает вероятность незаметной подмены установленной версии исходниками.

Пакет отдельно от корня

Создайте pyproject.toml и положите функцию в src/ledger/__init__.py. Ниже показаны оба файла. Минимальный пример использует setuptools как backend сборки; диапазон версий backend задайте согласно принятой в проекте политике. Важно, чтобы настройка обнаружения пакетов соответствовала выбранной структуре.

# pyproject.toml
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"

[project]
name = "ledger-example"
version = "0.1.0"
requires-python = ">=3.12"

[tool.setuptools.packages.find]
where = ["src"]

# src/ledger/__init__.py
def total(values: list[int]) -> int:
    return sum(values)

Проверка установленного wheel

Для разработки выполните python -m pip install -e . и проверьте import ledger. Для проверки поставки установите build командой python -m pip install build, затем выполните python -m build. Создайте чистое окружение и установите туда конкретный wheel из dist. Проверку импорта запустите из временного каталога вне репозитория: python -c "import ledger; print(ledger.total([2, 3]))". Ожидаемый ответ — 5.

Editable-установка удобна, но не заменяет проверку wheel. Backend может предоставлять доступ к файлам рабочего дерева, которые не попали в обычный дистрибутив. Поэтому импорт после editable install и импорт после установки wheel отвечают на разные вопросы. Первый помогает разрабатывать, второй проверяет то, что получит потребитель.

Посмотрите и на путь ledger.__file__. Он должен соответствовать выбранному окружению и форме установки. Если команда использует другой python, можно бесконечно переустанавливать пакет в правильное, но не участвующее в запуске окружение. Связка python -m pip делает используемый интерпретатор явным и уменьшает такую путаницу.

Отдельно проверьте не только верхний импорт, но и публичные подмодули, необходимые данные и точку входа. src layout не добавляет ресурсы в wheel автоматически. Он помогает обнаружить неправильное предположение о путях раньше. Полезный критерий готовности прост: чистое окружение запускает публичный сценарий без рабочего дерева и без ручного изменения PYTHONPATH.

Следующий шаг — перенести этот приём из отдельного файла в поддерживаемое приложение. В курсе «Python для продвинутых: ООП, типизация и тестирование» структура пакетов, объектная модель, mypy и pytest соединяются в типизированном трекере задач с хранением данных и логированием. Можно отработать связь между интерфейсом, реализацией и регрессионным тестом, а затем применить её к своему проекту.

Посмотреть программу и практику курса.

Документация: официальный источник по теме.

Другие задачи по теме: Типы, тесты и поведение программы.

Продолжить на практике

  • Python для продвинутых: ООП, типизация и тестирование ↗

    Практический курс-мост от базового Python к разработке поддерживаемых приложений. Вы разберётесь со структурой пакетов, объектной моделью, современной типизацией и автоматическими тестами, а затем соедините эти инструменты в небольшом приложении с хранением данных и логированием.