Методы создания распространяемых пакетов для языка Python

Раздел: Управление проектом -> Упаковка и дистрибуция

Сборка пакета Python: основные подходы

Вопрос: Как создать минимальный Python пакет для распространения?

Современным стандартом является использование pyproject.toml вместе с инструментом setuptools. Такой подход рекомендуется PEP 621 и считается основным.

Пример структуры пакета:

mypackage/\n├── pyproject.toml\n├── src/\n│   └── mypackage/\n│       └── __init__.py\n└── README.md

Python make package (создание пакета python)

Файл pyproject.toml содержит метаданные:

[build-system]\nrequires = ["setuptools>=61.0"]\nbuild-backend = "setuptools.build_meta"\n\n[project]\nname = "mypackage"\nversion = "0.1.0"\ndescription = "Пример пакета"\nreadme = "README.md"\nauthors = [{name = "Автор", email = "author@example.com"}]\nrequires-python = ">=3.8"\ndependencies = []

После настройки выполняется сборка:

python -m build

Команда создаёт дистрибутивы в папке dist/.

Типичная ошибка: пакет не обнаруживается, если исходный код расположен в корне проекта. Решение - явно указать [tool.setuptools.packages.find] или разместить код в src/.

Ошибка: ValueError: "version" must be a string - версия должна быть строкой, а не числом.

Альтернатива 1: использование Poetry

Poetry предлагает интегрированное управление зависимостями и упаковкой.

Пример инициализации:

poetry new mypackage

Файл pyproject.toml для Poetry:

[tool.poetry]\nname = "mypackage"\nversion = "0.1.0"\ndescription = ""\nauthors = ["Автор <author@example.com>"]\n\n[tool.poetry.dependencies]\npython = "^3.8"\n\n[build-system]\nrequires = ["poetry-core"]\nbuild-backend = "poetry.core.masonry.api"

Сборка выполняется командой:

poetry build

Проблема: Poetry создает виртуальное окружение автоматически. Если оно не нужно, следует отключить через настройки.

Ошибка: конфликт зависимостей при добавлении пакета - решается ручным разрешением или использованием poetry lock.

Альтернатива 2: Flit для быстрой публикации

Flit ориентирован на простые пакеты без сложных зависимостей.

Команда инициализации:

flit init

В результате создаётся pyproject.toml с конфигурацией Flit:

[build-system]\nrequires = ["flit_core>=3.2,<4"]\nbuild-backend = "flit_core.buildapi"\n\n[project]\nname = "mypackage"\nversion = "0.1.0"\ndescription = ""\nauthors = []

Для публикации используется:

flit publish

Проблема: Flit требует, чтобы код был в одной директории с pyproject.toml, и не поддерживает src-структуру по умолчанию.

Ошибка: отсутствие файла __init__.py - пакет не будет собран.

Расширенные примеры сборки и публикации

Ниже приведены примеры с подробными инструкциями и ожидаемыми выводами команд.

Пример: создание консольного скрипта с помощью entry_points

Для того чтобы после установки пакета можно было запустить команду из терминала, в pyproject.toml добавляется секция [project.scripts].

Пример
[project.scripts]\nmycli = "mypackage.cli:main"

После сборки и установки пакета:

Пример
pip install dist/mypackage-0.1.0-py3-none-any.whl

Выполнение команды:

Пример
mycli --help

Результат:

usage: mycli [-h]\noptions:\n  -h, --help  show this help message and exit

Пример: автоматическое управление версиями с setuptools_scm

Подключается плагин, который извлекает версию из Git тегов.

Пример
[build-system]\nrequires = ["setuptools>=61.0", "setuptools_scm>=6.0"]\nbuild-backend = "setuptools.build_meta"\n\n[project]\ndynamic = ["version"]\n\n[tool.setuptools_scm]\nwrite_to = "src/mypackage/_version.py"

После создания Git тега v0.1.0 и сборки:

Пример
python -m build

В файле _version.py появится строка:

version = "0.1.0"

Пример: публикация на TestPyPI через twine

После сборки дистрибутивов загружаются на тестовый индекс:

Пример
twine upload --repository testpypi dist/*

Процесс потребует ввода токена API. Успешная загрузка:

Uploading mypackage-0.1.0-py3-none-any.whl\n100% ━━━━━━━━━━━━━━━━━━━━ 6.3 kB/6.3 kB • 0:00:00 • ?\nUploading mypackage-0.1.0.tar.gz\n100% ━━━━━━━━━━━━━━━━━━━━ 5.2 kB/5.2 kB • 0:00:00 • ?

Пример: Poetry - добавление зависимости и сборка

Добавление пакета requests:

Пример
poetry add requests

В pyproject.toml появится:

Пример
[tool.poetry.dependencies]\npython = "^3.8"\nrequests = "^2.31"

Сборка:

Пример
poetry build

Вывод:

Building mypackage (0.1.0)\n  - Building sdist\n  - Built mypackage-0.1.0.tar.gz\n  - Building wheel\n  - Built mypackage-0.1.0-py3-none-any.whl

Пример: Flit - публикация с указанием описания из README

В pyproject.toml указывается:

Пример
[project]\nreadme = "README.md"

И публикация:

Пример
flit publish

Если не указан репозиторий, по умолчанию PyPI:

Found 1 file to publish: mypackage-0.1.0-py2.py3-none-any.whl\nUploading to https://pypi.org/legacy/\nUpload complete

Пример: включение дополнительных файлов с MANIFEST.in

Если в пакет нужно включить файлы, не являющиеся частью Python кода (например, конфигурации), создаётся MANIFEST.in:

Пример
include config/*.yaml\nrecursive-include data/ *.json

После сборки проверяется содержимое тарбола:

Пример
tar tzf dist/mypackage-0.1.0.tar.gz

Результат показывает включённые файлы.

src/mypackage/__init__.py\nsrc/mypackage/cli.py\nconfig/default.yaml\ndata/settings.json

Создание пакета Python - comments

En
Python make package (python)