← Назад к вопросам

Как создавать и публиковать свои пакеты на Python?

1.3 Junior🔥 241 комментариев
#Python

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Создание и публикация Python пакетов

Отличный вопрос про production-ready инструменты для распределения кода. Расскажу от нуля до PyPI.

Шаг 1: Структура пакета

Минимальная структура:

my_data_package/
├── setup.py                 # Конфигурация пакета (старый способ)
├── pyproject.toml          # Новый способ (PEP 517, 518)
├── README.md               # Описание
├── LICENSE                 # MIT, Apache, etc.
├── requirements.txt        # Зависимости для разработки
├── my_data_package/        # Основной пакет (директория с __init__.py)
│   ├── __init__.py
│   ├── core.py
│   ├── utils.py
│   └── dataframes.py
└── tests/                  # Тесты
    ├── __init__.py
    ├── test_core.py
    └── test_utils.py

Что такое __init__.py?

# my_data_package/__init__.py

__version__ = '0.1.0'
__author__ = 'Your Name'

# Экспортируем главные функции для удобства
from .core import process_data, transform_dataframe
from .utils import validate_input

__all__ = [
    'process_data',
    'transform_dataframe',
    'validate_input',
]

Использование пакета:

# После установки: pip install my_data_package

from my_data_package import process_data
# или
import my_data_package
my_data_package.process_data(data)

Шаг 2: Конфигурация пакета (современный способ - pyproject.toml)

PEP 517/518 - новый стандарт:

# pyproject.toml

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

[project]
name = "my-data-package"
version = "0.1.0"
description = "Data processing utilities for ETL pipelines"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Your Name", email = "your.email@example.com"},
]
keywords = ["data", "etl", "pipeline", "pandas"]
classifiers = [
    "Development Status :: 3 - Alpha",
    "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",
    "Topic :: Software Development :: Libraries",
]

[project.urls]
Homepage = "https://github.com/yourname/my-data-package"
Documentation = "https://my-data-package.readthedocs.io"
Repository = "https://github.com/yourname/my-data-package.git"
Issues = "https://github.com/yourname/my-data-package/issues"

[project.optional-dependencies]
dev = [
    "pytest>=6.0",
    "pytest-cov>=2.10",
    "black>=21.0",
    "ruff>=0.1.0",
    "mypy>=0.900",
]
docs = [
    "sphinx>=3.0",
    "sphinx-rtd-theme>=0.5.0",
]

[project.scripts]
process-data = "my_data_package.cli:main"

[tool.setuptools]
packages = ["my_data_package"]

[tool.black]
line-length = 100

[tool.ruff]
line-length = 100
select = ["E", "F", "W"]

Ключевые поля:

  • name — имя пакета (для PyPI)
  • version — текущая версия
  • requires-python — минимальная версия Python
  • dependencies — обязательные зависимости
  • optional-dependencies — дополнительные (dev, docs)
  • scripts — CLI команды

Шаг 3: Выложить на PyPI (Python Package Index)

Способ 1: Setup.py (старый, но всё ещё работает)

# setup.py

from setuptools import setup, find_packages

setup(
    name="my-data-package",
    version="0.1.0",
    description="Data processing utilities",
    author="Your Name",
    author_email="your.email@example.com",
    url="https://github.com/yourname/my-data-package",
    packages=find_packages(),
    python_requires=">=3.8",
    install_requires=[
        "pandas>=1.0.0",
        "numpy>=1.18.0",
        "pydantic>=1.0.0",
    ],
    extras_require={
        "dev": [
            "pytest>=6.0",
            "black>=21.0",
        ],
    },
    classifiers=[
        "Development Status :: 3 - Alpha",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
    ],
)

Способ 2: Только pyproject.toml (рекомендуется)

Просто используйте pyproject.toml выше — setup.py больше не нужен!

Шаг 4: Подготовка к публикации

1. Создаём аккаунт на PyPI

# Test PyPI (для тестирования)
https://test.pypi.org/account/register/

# Real PyPI (для production)
https://pypi.org/account/register/

2. Создаём credentials файл

# ~/.pypirc
[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...(ваш token)

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgEIcHlwaS5vcmc...(ваш test token)

3. Устанавливаем инструменты

# Инструменты для сборки пакета
pip install build twine

# build — собирает wheel и sdist
# twine — безопасно загружает на PyPI

Шаг 5: Сборка пакета

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

# Собираем пакет
python -m build

# Результат:
# dist/my_data_package-0.1.0-py3-none-any.whl
# dist/my_data_package-0.1.0.tar.gz

# Проверяем целостность
twine check dist/*

Что такое wheel и sdist?

.whl (wheel) — бинарный формат
- Быстро устанавливается (уже собран)
- Платформа-специфичный
- Пример: my_data_package-0.1.0-py3-none-any.whl

.tar.gz (source distribution) — исходный код
- Собирается при установке
- Кросс-платформенный
- Пример: my_data_package-0.1.0.tar.gz

Шаг 6: Публикация

На Test PyPI (для проверки):

# Загружаем на тестовый сервер
twine upload --repository testpypi dist/*

# Вводим credentials
# username: __token__
# password: pypi-AgEIcHlwaS5vcmc... (your token)

# Результат: https://test.pypi.org/project/my-data-package/

На Real PyPI (production):

# Загружаем на основной PyPI
twine upload dist/*

# Вводим credentials
# username: __token__
# password: pypi-AgEIcHlwaS5vcmc... (your production token)

# Результат: https://pypi.org/project/my-data-package/

Проверяем работу:

# Устанавливаем из PyPI
pip install my-data-package

# Импортируем
python -c "import my_data_package; print(my_data_package.__version__)"

# Output: 0.1.0

Шаг 7: Версионирование (Semantic Versioning)

Формат: MAJOR.MINOR.PATCH

0.1.0
│ │ └─ PATCH (0.1.0 → 0.1.1): bug fixes
│ └─── MINOR (0.1.0 → 0.2.0): new features, backward compatible
└───── MAJOR (0.1.0 → 1.0.0): breaking changes

Примеры:
0.1.0 — alpha version (ранний релиз)
0.5.0 — beta version (близко к stable)
1.0.0 — stable release
1.1.0 — minor feature
1.1.1 — bug fix
2.0.0 — major breaking change

Лучшая практика в коде:

# my_data_package/__init__.py

__version__ = "0.1.0"

# Используем в setup.py или pyproject.toml
from importlib.metadata import version
VERSION = version("my_data_package")

Практический пример: Полный цикл

1. Код пакета

# my_data_package/core.py

import pandas as pd
from pydantic import BaseModel, validator

class DataConfig(BaseModel):
    """Конфиг для обработки данных"""
    input_path: str
    output_path: str
    batch_size: int = 1000
    
    @validator('batch_size')
    def batch_size_positive(cls, v):
        if v <= 0:
            raise ValueError('batch_size must be positive')
        return v

def process_data(config: DataConfig) -> pd.DataFrame:
    """Обрабатываем данные из файла"""
    df = pd.read_csv(config.input_path)
    
    # Обработка
    df['processed'] = df['value'].rolling(window=3).mean()
    
    # Сохранение
    df.to_parquet(config.output_path)
    
    return df

2. Тесты

# tests/test_core.py

import pytest
import tempfile
import pandas as pd
from my_data_package import process_data, DataConfig

def test_process_data():
    # Создаём тестовые данные
    with tempfile.NamedTemporaryFile(suffix='.csv') as f:
        df = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
        df.to_csv(f.name, index=False)
        
        config = DataConfig(
            input_path=f.name,
            output_path="/tmp/output.parquet"
        )
        
        result = process_data(config)
        
        assert len(result) == 5
        assert 'processed' in result.columns

def test_invalid_batch_size():
    with pytest.raises(ValueError):
        DataConfig(
            input_path="test.csv",
            output_path="out.parquet",
            batch_size=-1  # Invalid!
        )

3. README.md

# My Data Package

[![PyPI version](https://img.shields.io/pypi/v/my-data-package.svg)](https://pypi.org/project/my-data-package/)
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)

Дата-пакет для обработки больших объёмов данных в ETL пайплайнах.

## Установка

```bash
pip install my-data-package

Быстрый старт

from my_data_package import process_data, DataConfig

config = DataConfig( input_path="data.csv", output_path="output.parquet" )

result = process_data(config) print(result.head())

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

https://my-data-package.readthedocs.io


**4. Публикуем**

```bash
# Сборка
python -m build

# Проверка
twine check dist/*

# Загрузка
twine upload dist/*

# Проверяем
pip install --upgrade my-data-package

Автоматизация CI/CD для публикации

GitHub Actions для автоматической публикации

# .github/workflows/publish.yml

name: Publish to PyPI

on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.10'
    
    - name: Install build tools
      run: |
        python -m pip install --upgrade pip
        pip install build twine
    
    - name: Build package
      run: python -m build
    
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        password: ${{ secrets.PYPI_API_TOKEN }}

Использование:

# Тегируем версию
git tag v0.1.0
git push origin v0.1.0

# GitHub Actions автоматически публикует!

Best Practices

  1. Версионирование: Используй Semantic Versioning (MAJOR.MINOR.PATCH)
  2. Зависимости: Указывай диапазон версий, не конкретные
    ✅ pandas>=1.0.0,<2.0.0
    ❌ pandas==1.3.2
    
  3. Документация: README + docstrings + примеры
  4. Тесты: 100% coverage пакета
  5. CI/CD: Автоматическая проверка и публикация
  6. CHANGELOG: Документируй изменения между версиями

Заключение

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

  1. Структура пакета (my_package/init.py)
  2. pyproject.toml конфигурация
  3. setup.py (опционально)
  4. Сборка: python -m build
  5. Публикация: twine upload dist/*
  6. Проверка: pip install my-package

Для Data Engineer это критично для переиспользуемого кода, общих утилит, и распределения инструментов в team'е.