Создание пакетов Python: руководство от разработки до PyPI

Освойте упаковку Python от кода до развертывания на PyPI

Содержимое страницы

Упаковка Python значительно эволюционировала, и современные инструменты и стандарты делают распространение вашего кода проще, чем когда-либо.

Это руководство проведет вас через создание профессиональных пакетов Python и их публикацию в PyPI. Если вы новичок в Python или вам нужна быстрая справка, ознакомьтесь с нашим Шпаргалкой по Python, чтобы быстро освоить основы Python.

Python Packages

Почему стоит упаковывать ваш код Python?

Упаковка вашего проекта на Python предлагает множество преимуществ:

  • Переиспользуемость: Делитесь кодом между несколькими проектами без копирования
  • Распространение: Позволяйте другим устанавливать ваш код с помощью простой команды pip install
  • Управление зависимостями: Четко указывайте и управляйте зависимостями
  • Версионирование: Отслеживайте релизы и поддерживайте обратную совместимость
  • Профессиональные стандарты: Следуйте лучшим практикам сообщества Python
  • Документация: Структура поощряет правильное документирование и тестирование

Современная структура пакета Python

Хорошо организованный пакет следует этой структуре:

my-package/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_core.py
├── docs/
│   └── index.md
├── .github/
│   └── workflows/
│       └── publish.yml
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore

src-layout теперь предпочтительнее плоской структуры, потому что он:

  • Предотвращает случайные импорты неустановленного кода во время разработки
  • Делает тестирование более надежным, заставляя выполнять установку
  • Четко разделяет исходный код и другие файлы проекта

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

Файл pyproject.toml: Современная конфигурация

pyproject.toml (PEP 518, 621) - это современный стандарт конфигурации проекта Python, заменяющий устаревший setup.py:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
version = "0.1.0"
description = "Отличный пакет Python"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Ваше Имя", email = "you@example.com"}
]
keywords = ["пример", "учебник", "пакет"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
    "mypy>=1.5.0",
]
docs = [
    "sphinx>=7.0.0",
    "sphinx-rtd-theme>=1.3.0",
]

[project.urls]
Homepage = "https://github.com/yourusername/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/yourusername/mypackage"
Issues = "https://github.com/yourusername/mypackage/issues"

[project.scripts]
mypackage-cli = "mypackage.cli:main"

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

Ключевые разделы конфигурации

  1. build-system: Указывает бэкенд сборки (setuptools, hatchling, poetry-core, flit-core)
  2. project: Основные метаданные и зависимости
  3. project.optional-dependencies: Дополнительные зависимости для функций (разработка, документация, тестирование)
  4. project.scripts: Точки входа командной строки
  5. tool.*: Конфигурация для конкретных инструментов (black, pytest, mypy и т.д.)

Выбор бэкенда сборки

Setuptools (Стандартный выбор)

Наиболее широко используемый и совместимый вариант:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

Преимущества: Универсальная совместимость, обширные функции, большое количество плагинов Недостатки: Более громоздкая конфигурация, медленнее, чем новые альтернативы

Hatchling (Современный и быстрый)

Легковесный и производительный современный бэкенд:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Преимущества: Быстрая сборка, простая конфигурация, хорошие настройки по умолчанию Недостатки: Меньше плагинов, чем у setuptools

Poetry (Все-в-одном)

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

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "Отличный пакет Python"
authors = ["Ваше Имя <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.28.0"

Преимущества: Решение зависимостей, файлы блокировки, интегрированный рабочий процесс Недостатки: Различный рабочий процесс, большая поверхность инструмента

Flit (Минималистичный)

Простой инструмент для простых пакетов:

[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"

Преимущества: Очень простой, минимальная конфигурация Недостатки: Ограниченные функции для сложных пакетов

Сборка вашего пакета

Установка инструментов сборки

# Установка современного инструмента сборки
pip install build

# Или для Poetry
pip install poetry

# Или для Hatchling
pip install hatch

Сборка дистрибутивных файлов

Используя пакет build (работает с любым бэкендом):

# Очистка предыдущих сборок
rm -rf dist/ build/ *.egg-info

# Сборка исходного дистрибутива и колеса
python -m build

# Это создает:
# dist/mypackage-0.1.0.tar.gz    (исходный дистрибутив)
# dist/mypackage-0.1.0-py3-none-any.whl  (колесо)

Используя Poetry:

poetry build

Используя Hatch:

hatch build

Понимание форматов дистрибутивов

Исходный дистрибутив (sdist) - .tar.gz

  • Содержит исходный код и инструкции по сборке
  • Пип пользователей собирает его во время установки
  • Включает тесты, документацию и другие файлы разработки

Колесо - .whl

  • Предварительно собранный бинарный дистрибутив
  • Быстрая установка (без этапа сборки)
  • Платформено-специфичный или чистый Python
  • Рекомендуемый формат для распространения

Публикация в PyPI

Метод 1: Ручная загрузка с Twine (Традиционный)

# Установка twine
pip install twine

# Проверка пакета перед загрузкой
twine check dist/*

# Сначала загрузите в TestPyPI (рекомендуется)
twine upload --repository testpypi dist/*

# Тестирование установки из TestPyPI
pip install --index-url https://test.pypi.org/simple/ mypackage

# Загрузка в производственный PyPI
twine upload dist/*

Настройка учетных данных в ~/.pypirc:

[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgENdGVzdC5weXBpLm9yZw...

Метод 2: Доверенная публикация (Рекомендуется для CI/CD)

Доверенная публикация использует OpenID Connect (OIDC) для аутентификации из платформ CI/CD без хранения токенов.

Настройка в PyPI:

  1. Перейдите в настройки вашего проекта PyPI
  2. Перейдите в раздел “Publishing”
  3. Добавьте нового “pending publisher”
  4. Настройте:
    • Owner: Ваше имя пользователя или организация GitHub
    • Repository: Имя репозитория
    • Workflow: publish.yml
    • Environment: release (необязательно, но рекомендуется)

GitHub Actions Workflow (.github/workflows/publish.yml):

name: Publish to PyPI

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip
          pip install build          

      - name: Build package
        run: python -m build

      - name: Store distribution packages
        uses: actions/upload-artifact@v3
        with:
          name: python-package-distributions
          path: dist/

  publish:
    needs: build
    runs-on: ubuntu-latest
    environment: release
    permissions:
      id-token: write

    steps:
      - name: Download distributions
        uses: actions/download-artifact@v3
        with:
          name: python-package-distributions
          path: dist/

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1

Преимущества:

  • Нет токенов API для управления и защиты
  • Автоматическая аутентификация через OIDC
  • Повышенная безопасность за счет правил защиты сред
  • Журнал аудита всех публикаций

Лучшие практики

1. Управление версиями

Используйте семантическое версионирование (SemVer): MAJOR.MINOR.PATCH

# Установка инструмента для изменения версий
pip install bump2version

# Изменение версии
bump2version patch  # 0.1.0 -> 0.1.1
bump2version minor  # 0.1.1 -> 0.2.0
bump2version major  # 0.2.0 -> 1.0.0

Настройка в .bumpversion.cfg:

[bumpversion]
current_version = 0.1.0
commit = True
tag = True

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:src/mypackage/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

2. Включение обязательных файлов

README.md: Четкое описание проекта, инструкции по установке, примеры использования LICENSE: Выберите подходящую лицензию (MIT, Apache 2.0, GPL и т.д.) CHANGELOG.md: Документируйте изменения между версиями .gitignore: Исключите артефакты сборки, кэши, виртуальные окружения

3. Комплексное тестирование

# Установка зависимостей для тестирования
pip install pytest pytest-cov

# Запуск тестов с покрытием
pytest --cov=mypackage tests/

# Генерация отчета о покрытии
pytest --cov=mypackage --cov-report=html tests/

Настройка в pyproject.toml:

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "--strict-markers --cov=mypackage"

4. Инструменты контроля качества кода

# Форматирование
black src/ tests/

# Проверка кода
flake8 src/ tests/

# Проверка типов
mypy src/

# Сортировка импортов
isort src/ tests/

Интеграция в pre-commit хуки (.pre-commit-config.yaml):

repos:
  - repo: https://github.com/psf/black
    rev: 23.9.1
    hooks:
      - id: black

  - repo: https://github.com/pycqa/flake8
    rev: 6.1.0
    hooks:
      - id: flake8

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.5.1
    hooks:
      - id: mypy

5. Документация

Используйте Sphinx для документации:

# Установка Sphinx
pip install sphinx sphinx-rtd-theme

# Инициализация документации
cd docs
sphinx-quickstart

# Сборка документации
make html

Или используйте MkDocs для более простой документации на основе Markdown:

pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve

6. Непрерывная интеграция

Тестирование ваших Python-пакетов на разных платформах и в разных средах критически важно для надежности. Для получения информации о производительности Python в разных сценариях развертывания, см. наше сравнение Производительность AWS Lambda: JavaScript vs Python vs Golang, которое исследует производительность Python в серверных средах.

Полный workflow CI/CD (.github/workflows/ci.yml):

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

    steps:
      - uses: actions/checkout@v4

      - name: Настройка Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}

      - name: Установка зависимостей
        run: |
          python -m pip install --upgrade pip
          pip install -e ".[dev]"          

      - name: Запуск тестов
        run: pytest --cov=mypackage --cov-report=xml

      - name: Загрузка покрытия
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Настройка Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Установка зависимостей
        run: |
          pip install black flake8 mypy          

      - name: Проверка форматирования Black
        run: black --check src/ tests/

      - name: Flake8
        run: flake8 src/ tests/

      - name: MyPy
        run: mypy src/

Общие проблемы и решения

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

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

Решение: Убедитесь в правильной структуре пакета с файлами __init__.py и правильной настройке pyproject.toml:

[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]

Проблема 2: Отсутствующие зависимости

Проблема: Пакет устанавливается, но падает при запуске из-за отсутствующих зависимостей

Решение: Укажите все зависимости в pyproject.toml:

[project]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

Проблема 3: Конфликты версий

Проблема: Пакет работает в разработке, но падает в продакшене

Решение: Используйте виртуальные окружения и указывайте минимальные версии:

# Создание изолированного окружения
python -m venv .venv
source .venv/bin/activate  # На Windows: .venv\Scripts\activate

# Установка в режиме разработки
pip install -e ".[dev]"

Проблема 4: Большой размер пакета

Проблема: Пакет слишком долго скачивается/устанавливается

Решение: Исключите ненужные файлы с помощью MANIFEST.in:

include README.md
include LICENSE
include pyproject.toml
recursive-include src *.py
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-exclude tests *
recursive-exclude docs *

Проблема 5: Платформенные проблемы

Проблема: Пакет работает на одной ОС, но падает на другой

Решение: Тестируйте на нескольких платформах с помощью матричных сборок CI/CD (см. пример CI выше)

Чек-лист перед публикацией

Перед публикацией пакета в PyPI проверьте:

  • Все тесты проходят на разных версиях Python
  • Код отформатирован и проверен
  • README.md полный с примерами
  • Файл LICENSE включен
  • Номер версии обновлен
  • CHANGELOG.md обновлен
  • Зависимости правильно указаны
  • Пакет собирается без ошибок (python -m build)
  • Пакет протестирован на TestPyPI
  • Документация актуальна
  • Репозиторий Git помечен версией
  • Создан релиз на GitHub (для доверенной публикации)

Дополнительные темы

Точки входа и CLI-инструменты

Создание командной строки:

[project.scripts]
mypackage = "mypackage.cli:main"

Реализация в src/mypackage/cli.py:

import click

@click.command()
@click.option('--name', default='World', help='Имя для приветствия')
def main(name):
    """Пример простого CLI-инструмента"""
    click.echo(f'Привет, {name}!')

if __name__ == '__main__':
    main()

Для реального примера создания Python-пакетов, взаимодействующих с внешними API, см. наше руководство по Интеграции Ollama с Python, которое демонстрирует создание Python-клиентов с интеграцией как REST API, так и официальной библиотеки.

Системы плагинов

Включение обнаружения плагинов:

[project.entry-points."mypackage.plugins"]
plugin_a = "mypackage_plugin_a:PluginA"

Расширения на C

Для критического кода:

[tool.setuptools.ext-modules]
name = "mypackage._speedups"
sources = ["src/mypackage/_speedups.c"]

Пространства имен пакетов

Для распределенных пакетов под общим пространством имен:

[tool.setuptools.packages.find]
where = ["src"]
include = ["company.*"]

[tool.setuptools.package-data]
"company.mypackage" = ["py.typed"]

Полезные инструменты и ресурсы

Инструменты разработки пакетов

  • cookiecutter: Шаблоны проектов для Python-пакетов
  • tox: Автоматизация тестирования на разных версиях Python
  • nox: Гибкая альтернатива tox
  • pre-commit: Фреймворк для git-хуков качества кода
  • commitizen: Стандартизация сообщений коммитов и версионирования

Платформы документации

  • Read the Docs: Бесплатный хостинг документации
  • GitHub Pages: Хостинг документации MkDocs или Sphinx
  • pdoc: Простой генератор документации API

Реестры пакетов

  • PyPI: Официальный индекс Python-пакетов (pypi.org)
  • TestPyPI: Тестовая среда (test.pypi.org)
  • Anaconda.org: Распределение пакетов Conda
  • GitHub Packages: Приватный хостинг пакетов

Мониторинг и аналитика

  • PyPI Stats: Статистика загрузок пакетов
  • Libraries.io: Мониторинг зависимостей и оповещения
  • Snyk: Сканирование на уязвимости безопасности
  • Dependabot: Автоматические обновления зависимостей

Заключение

Современная упаковка Python стала более дружелюбной для разработчиков с такими стандартами, как pyproject.toml, мощными бэкендами сборки и безопасной публикацией через Trusted Publishing. Следуя этому руководству, вы можете создавать профессиональные, поддерживаемые Python-пакеты, которые эффективно обслуживают ваших пользователей.

Основные выводы:

  1. Используйте pyproject.toml для всех новых проектов
  2. Выбирайте подходящий бэкенд сборки для ваших нужд
  3. Реализуйте автоматизированное тестирование и CI/CD
  4. Используйте Trusted Publishing для безопасной, безтокенной развертки
  5. Следуйте семантическому версионированию и ведите changelogs
  6. Предоставляйте полную документацию и примеры

Экосистема упаковки Python продолжает улучшаться с лучшими инструментами, стандартами и функциями безопасности. Оставайтесь в курсе Python Enhancement Proposals (PEPs) и лучших практик сообщества, чтобы поддерживать ваши пакеты современными и поддерживаемыми.

Полезные ссылки

Другие полезные статьи на этом сайте