Как работает пакетирование библиотек?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Пакетирование библиотек в Python: полное руководство
Пакетирование (packaging) — это процесс подготовки Python кода для распространения и установки. За 10+ лет я видел эволюцию этого процесса от distutils к современным инструментам, и это критический навык для любого профессионального разработчика.
Структура Python пакета
Перед пакетированием нужна правильная структура:
my_library/
├── src/
│ └── my_library/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ ├── test_core.py
│ └── test_utils.py
├── docs/
│ └── README.md
├── pyproject.toml
├── setup.py (опционально для обратной совместимости)
├── setup.cfg
├── LICENSE
└── README.md
Шаг 1: Конфигурация через pyproject.toml
Современный стандарт — использовать pyproject.toml (PEP 517, 518, 621):
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-library"
version = "0.1.0"
description = "A brief description"
authors = [{name = "Your Name", email = "you@example.com"}]
license = {text = "MIT"}
requires-python = ">=3.8"
dependencies = [
"requests>=2.28.0",
"pydantic>=2.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
"ruff>=0.1.0",
]
docs = [
"sphinx>=5.0",
"sphinx-rtd-theme>=1.0",
]
[project.urls]
Homepage = "https://github.com/yourname/my-library"
Documentation = "https://my-library.readthedocs.io"
Repository = "https://github.com/yourname/my-library"
[tool.setuptools]
package-dir = {"" = "src"}
[tool.setuptools.packages.find]
where = ["src"]
Шаг 2: Создание дистрибутивов
SETUPTOOLS генерирует два типа пакетов:
Source Distribution (sdist) — исходный код:
python -m build --sdist
# Результат: dist/my-library-0.1.0.tar.gz
# Содержит: весь исходный код, тесты, документация
Wheel (bdist) — готовый к установке:
python -m build --wheel
# Результат: dist/my_library-0.1.0-py3-none-any.whl
# Содержит: скомпилированный код, метаданные, готов к установке
Полная сборка (оба типа):
python -m build
# Создаёт оба: .tar.gz и .whl
Шаг 3: Метаданные пакета
Внутри каждого пакета находится папка *.dist-info с метаданными:
my_library-0.1.0.dist-info/
├── METADATA # Информация о пакете (имя, версия, зависимости)
├── WHEEL # Информация о wheel
├── entry_points.txt # CLI команды пакета
├── top_level.txt # Основные модули
└── RECORD # Список всех файлов с хешами
Пример METADATA:
Metadata-Version: 2.1
Name: my-library
Version: 0.1.0
Summary: A brief description
Author: Your Name
Author-email: you@example.com
Home-page: https://github.com/yourname/my-library
Requires-Python: >=3.8
Requires-Dist: requests (>=2.28.0)
Requires-Dist: pydantic (>=2.0)
Шаг 4: Загрузка на PyPI
# Установить twine для загрузки
pip install twine
# Загрузить на PyPI (требует аккаунта)
twine upload dist/*
# Или на тестовый PyPI для проверки
twine upload --repository testpypi dist/*
Шаг 5: Установка пакета
После загрузки пользователи могут установить:
pip install my-library
# pip находит пакет на PyPI, скачивает wheel, распаковывает
Механизм установки (как pip работает)
# 1. Разрешение зависимостей (dependency resolution)
# pip читает METADATA и рекурсивно разрешает зависимости
# 2. Скачивание
# pip скачивает .whl (или .tar.gz, затем собирает wheel)
# 3. Распаковка и установка
# Распаковывает в site-packages/
# my_library/
# my_library-0.1.0.dist-info/
# 4. Обновление PATH
# Если есть скрипты (entry_points) — создаёт CLI команды
Entry Points: создание CLI команд
[project.scripts]
my-app = "my_library.cli:main"
[project.gui-scripts]
my-gui = "my_library.gui:launch"
Тогда после установки доступна команда:
$ my-app --help # Вызывает функцию main() из my_library.cli
Реализация:
# my_library/cli.py
def main():
print("Hello from CLI!")
if __name__ == "__main__":
main()
Управление версиями
Семантическое версионирование (SemVer):
MAJOR.MINOR.PATCH
0.1.5
│ │ └─ Патч (исправления багов, не меняет API)
│ └──── Минор (новые фичи, обратно совместимо)
└────── Мажор (ломающие изменения)
Автоматизация версий с setuptools_scm:
[tool.setuptools_scm]
# Версия определяется из git tags автоматически
# git tag v0.2.0
# версия = 0.2.0
Развитие пакета (editable install)
Для разработки:
pip install -e .[dev]
# Устанавливает пакет в режиме редактирования
# Изменения в src/ видны сразу без переустановки
Файл setup.py для старых систем
Хотя современный стандарт — pyproject.toml, иногда нужен setup.py для совместимости:
from setuptools import setup, find_packages
setup(
name="my-library",
version="0.1.0",
packages=find_packages(where="src"),
package_dir={"": "src"},
python_requires=">=3.8",
install_requires=[
"requests>=2.28.0",
"pydantic>=2.0",
],
)
Полный workflow примера
# 1. Создание проекта
mkdir my-library && cd my-library
mkdir -p src/my_library tests
# 2. Написание кода и тестов
echo 'def hello(): return "Hello!"' > src/my_library/core.py
# 3. Создание pyproject.toml (как выше)
# 4. Локальная разработка
pip install -e .[dev]
pytest # Запуск тестов
# 5. Сборка пакета
pip install build
python -m build
ls dist/ # Получим .tar.gz и .whl
# 6. Проверка на test PyPI
twine upload --repository testpypi dist/*
# 7. Финальная загрузка на PyPI
twine upload dist/*
Инструменты экосистемы
| Инструмент | Назначение |
|---|---|
| build | Универсальная сборка |
| setuptools | Основной инструмент для сборки |
| wheel | Формат и инструмент для wheel |
| twine | Безопасная загрузка на PyPI |
| setuptools_scm | Версионирование из git |
| flit | Упрощённая альтернатива setuptools |
| poetry | All-in-one: dependency management + packaging |
Итого: как работает пакетирование
- Структурируем код (src layout)
- Определяем метаданные (pyproject.toml)
- Собираем дистрибутивы (sdist + wheel)
- Загружаем на PyPI (twine)
- Пользователи устанавливают (pip install)
- Pip распаковывает в site-packages
Это стандартный, надёжный процесс, используемый миллионами разработчиков ежедневно.