Как создавать и публиковать свои пакеты на Python?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Создание и публикация 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— минимальная версия Pythondependencies— обязательные зависимости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
[](https://pypi.org/project/my-data-package/)
[](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
- Версионирование: Используй Semantic Versioning (MAJOR.MINOR.PATCH)
- Зависимости: Указывай диапазон версий, не конкретные
✅ pandas>=1.0.0,<2.0.0 ❌ pandas==1.3.2 - Документация: README + docstrings + примеры
- Тесты: 100% coverage пакета
- CI/CD: Автоматическая проверка и публикация
- CHANGELOG: Документируй изменения между версиями
Заключение
Процесс публикации пакета:
- Структура пакета (my_package/init.py)
- pyproject.toml конфигурация
- setup.py (опционально)
- Сборка:
python -m build - Публикация:
twine upload dist/* - Проверка:
pip install my-package
Для Data Engineer это критично для переиспользуемого кода, общих утилит, и распределения инструментов в team'е.