pyproject.toml и src-layout: структура Python-проекта
Файл pyproject.toml хранит стандартные метаданные проекта и сообщает инструментам, какой build backend использовать. Раскладка src помещает импортируемый пакет не рядом с конфигурацией, а в отдельный каталог src. Вместе эти решения отделяют исходный код пакета от служебных файлов репозитория и заставляют разработчика проверять установленный проект, а не случайную копию из текущего каталога.
Минимальное дерево проекта
Для небольшого консольного приложения структура может выглядеть так:
word-counter/
├── pyproject.toml
├── README.md
├── src/
│ └── word_counter/
│ ├── __init__.py
│ └── cli.py
└── tests/Имя дистрибутива в метаданных может содержать дефис, а импортируемый пакет обычно использует подчёркивание. Это разные пространства имён: пользователь устанавливает дистрибутив word-counter-demo, а код выполняет import word_counter.
Что описывает pyproject.toml
Секция build-system задаёт инструменты, необходимые для сборки, и backend. Секция project содержит имя, версию, требуемую версию Python и другие метаданные. Настройки конкретного backend располагаются в его собственной секции.
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "word-counter-demo"
version = "0.1.0"
description = "Учебный счётчик слов"
readme = "README.md"
requires-python = ">=3.10"
[project.scripts]
word-count = "word_counter.cli:main"
[tool.setuptools.packages.find]
where = ["src"]Таблица project.scripts объявляет консольную точку входа. После установки инструмент создаёт команду word-count, которая импортирует функцию main; сам TOML не выполняет Python-код.
Установка src-layout в режиме разработки
Пусть src/word_counter/cli.py содержит рабочий минимальный пример:
def count_words(text):
return len(text.split())
def main():
sample = "ясная структура проекта"
print(f"words={count_words(sample)}")В активном окружении из корня проекта выполните:
python -m pip install -e .
word-count
python -c "import word_counter; print(word_counter.__file__)"Ожидаемый первый результат — words=3, а затем путь внутри src/word_counter. Editable-установка связывает окружение с рабочими исходниками, поэтому изменения видны без создания нового wheel. Она всё равно является установкой и должна выполняться в выбранной .venv.
Зачем отделять src
При плоской раскладке корень репозитория часто попадает в путь поиска, и импорт может пройти даже тогда, когда пакет не включён в сборку или забыта нужная конфигурация. В src-layout команда из корня не находит пакет только по соседству; требуется корректная установка. Это раньше обнаруживает различия между локальной разработкой и тем, что получит пользователь.
Каталоги tests, документация и конфигурация не становятся подмодулями приложения случайно. Однако src не исправляет неверные метаданные автоматически: нужно проверить, какие файлы вошли в wheel, и прогнать тесты на собранном артефакте.
Как диагностировать сломанный src-layout
Если до установки команда отвечает ModuleNotFoundError: No module named 'word_counter', для src-layout это ожидаемый сигнал, а не повод добавить src в sys.path внутри программы. Проверьте активный Python, выполните python -m pip install -e . и затем python -m pip show word-counter-demo. Если установка прошла, но пакет всё ещё отсутствует, исследуйте секцию поиска пакетов и реальное имя каталога. Ошибка создания команды требует дополнительно проверить строку word_counter.cli:main: модуль должен импортироваться, а объект main существовать.
Собираем и устанавливаем учебный пакет
Соберите указанное дерево с функцией count_words, установите проект editable-режимом и вызовите консольную команду из каталога вне репозитория. Добавьте тест, который импортирует функцию по публичному имени. Для самостоятельной проверки временно измените where = ["src"] на несуществующий каталог, переустановите проект и объясните результат диагностики; затем восстановите конфигурацию и убедитесь, что импорт указывает на рабочий src.
Проверка структуры перед сборкой
pyproject.tomlописывает сборку и метаданные, но выбор backend остаётся явным.- Имя устанавливаемого дистрибутива не обязано совпадать посимвольно с именем импорта.
src-layout помогает тестировать установленный пакет, а не случайный путь из checkout.- Editable-режим удобен в разработке, но финальный артефакт всё равно следует собирать и проверять.
Вопросы о pyproject.toml и src-layout
pyproject.toml заменяет requirements.txt?
Не всегда. Метаданные проекта описывают зависимости устанавливаемого дистрибутива, а requirements-файл может фиксировать целое окружение приложения или инструменты разработки. Их роли пересекаются, но не тождественны.
Обязательно ли применять src-layout?
Нет, Python поддерживает и плоскую структуру. src полезен, когда важно рано выявлять неявные импорты из корня и чётко отделять пакет от остальных файлов репозитория.
Нужно ли добавлять src в переменную PYTHONPATH?
Для обычного процесса разработки лучше установить проект в виртуальное окружение. Постоянный ручной PYTHONPATH скрывает ошибки конфигурации сборки и делает запуск зависимым от внешнего состояния shell.