Перейти к содержимому
pythonСредний75 минут

Poetry и uv: современное управление зависимостями в Python

Полное практическое руководство по Poetry и uv — от базовой установки до продвинутых техник управления зависимостями, монорепозиториями и CI/CD

#python#poetry#uv#зависимости#пакеты#packaging#dependency management#монорепозиторий#ci-cd#pip

Poetry и uv: современное управление зависимостями в Python

Poetry и uv решают одну проблему разными способами: первый — швейцарский нож для packaging (build, publish, deps), второй — болид Формулы-1 на Rust (скорость превыше всего). Оба убивают pip/requirements.txt, но по-разному.

Как пользоваться материалом

  • Спешишь? Читай введение, быстрый старт с Poetry и сравнительную таблицу.
  • Мигрируешь с pip? Смотри разделы "Миграция с requirements.txt" для обоих инструментов.
  • Публикуешь пакеты? Изучи разделы про packaging и CI/CD интеграцию.
  • Работаешь в команде? Обрати внимание на lockfiles и практики для production.
  • Хочешь максимальную скорость? Переходи сразу к разделу про uv.

Глоссарий

Dependency Management - Управление зависимостями проекта (библиотеки, версии, конфликты).

Lock File - Файл с точными версиями всех зависимостей для воспроизводимых установок.

Virtual Environment (venv) - Изолированное окружение Python со своим набором пакетов.

Poetry - Инструмент для управления зависимостями и пакетами Python. Работает с pyproject.toml.

uv - Ультрабыстрый менеджер пакетов и проектов Python на Rust (замена pip, pip-tools, virtualenv).

pyproject.toml - Современный стандарт конфигурации Python проектов (PEP 518/621).

requirements.txt - Старый способ указания зависимостей (используется pip).

pip-tools - Расширение pip для генерации lock файлов из requirements.in.

Semantic Versioning - Система версионирования вида MAJOR.MINOR.PATCH (2.1.3).

Version Constraint - Ограничение версии (^1.2.0, >=2.0.0, ~=3.1).

Transitive Dependencies - Зависимости ваших зависимостей (непрямые зависимости).

Monorepo - Один репозиторий с несколькими связанными проектами/пакетами.

Workspace - Группа связанных пакетов в monorepo с общими зависимостями.

Build Backend - Инструмент для сборки Python пакетов (setuptools, poetry-core, hatchling).

Wheel - Бинарный формат распространения Python пакетов (.whl).

Source Distribution (sdist) - Исходный код пакета для сборки (.tar.gz).

Введение: Проблемы pip и requirements.txt

Что не так с pip?

# ❌ Классический способ (проблемы)
pip install requests
pip freeze > requirements.txt
 
# Проблемы:
# 1. Нет разделения dev/prod зависимостей
# 2. requirements.txt не содержит метаданных проекта
# 3. Нет автоматического разрешения конфликтов версий
# 4. Транзитивные зависимости захламляют файл
# 5. Ручное управление виртуальными окружениями

Пример проблемы:

# requirements.txt (100+ строк транзитивных зависимостей)
requests==2.31.0
urllib3==2.1.0        # ← Зависимость requests
certifi==2023.11.17   # ← Зависимость requests
charset-normalizer==3.3.2  # ← Зависимость requests
idna==3.6            # ← Зависимость requests

Вы хотели только requests, но получили 5 строк. Через год проект имеет 200+ строк в requirements.txt, где 90% — транзитивные зависимости.

Современные решения

Poetry — полнофункциональный инструмент:

  • ✅ Единый файл pyproject.toml с метаданными
  • ✅ Автоматическое управление venv
  • ✅ Lock файл для воспроизводимости
  • ✅ Разделение dev/prod зависимостей
  • ✅ Встроенный packaging и публикация в PyPI

uv — скорость превыше всего:

  • ✅ В 10-100 раз быстрее pip
  • ✅ Совместим с pip, Poetry, pip-tools
  • ✅ Написан на Rust (zero overhead)
  • ✅ Универсальный инструмент (замена pip, venv, pip-tools)
  • ✅ Кроссплатформенный (одинаково быстр везде)

Poetry: Полнофункциональный менеджер

Poetry — стандарт де-факто для современных Python проектов. Если пишешь библиотеку или нужен packaging — это твой выбор.

Установка Poetry

# Рекомендуемый способ (официальный)
curl -sSL https://install.python-poetry.org | python3 -
 
# Или через pipx (изолированная установка)
pipx install poetry
 
# Или через Homebrew (macOS/Linux)
brew install poetry
 
# Проверка установки
poetry --version
# Poetry (version 1.7.1)
 
# Настройка: создавать venv внутри проекта
poetry config virtualenvs.in-project true

Быстрый старт: Создание проекта

# ========================================
# Создание нового проекта
# ========================================
 
poetry new my-project
cd my-project
 
# Структура проекта:
# my-project/
#   ├── my_project/        # Исходный код
#   │   └── __init__.py
#   ├── tests/            # Тесты
#   │   └── __init__.py
#   ├── pyproject.toml    # Конфигурация
#   └── README.md
 
# ========================================
# Или инициализация в существующем проекте
# ========================================
 
mkdir my-app && cd my-app
poetry init  # Интерактивный мастер
 
# Отвечаете на вопросы:
# - Имя пакета
# - Версия
# - Описание
# - Автор
# - Лицензия
# - Зависимости

Структура pyproject.toml

[tool.poetry]
name = "my-project"
version = "0.1.0"
description = "Amazing Python project"
authors = ["Your Name <you@example.com>"]
license = "MIT"
readme = "README.md"
homepage = "https://github.com/user/my-project"
repository = "https://github.com/user/my-project"
keywords = ["python", "awesome"]
 
# Python версии
python = "^3.9"  # >= 3.9, < 4.0
 
[tool.poetry.dependencies]
# Продакшн зависимости
python = "^3.9"
requests = "^2.31.0"       # >= 2.31.0, < 3.0.0
fastapi = "^0.104.0"
sqlalchemy = {extras = ["asyncio"], version = "^2.0.0"}
 
[tool.poetry.group.dev.dependencies]
# Dev зависимости (не попадут в prod)
pytest = "^7.4.0"
black = "^23.11.0"
mypy = "^1.7.0"
ruff = "^0.1.6"
 
[tool.poetry.group.docs.dependencies]
# Документация (опциональная группа)
sphinx = "^7.2.0"
sphinx-rtd-theme = "^2.0.0"
 
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Управление зависимостями

# ========================================
# Добавление зависимостей
# ========================================
 
# Продакшн зависимость
poetry add requests
poetry add "fastapi>=0.100.0"
poetry add django@^4.2  # Конкретная версия
 
# Dev зависимость
poetry add --group dev pytest black mypy
 
# Опциональная зависимость
poetry add --optional sphinx
 
# С extras
poetry add "sqlalchemy[asyncio]"
 
# ========================================
# Удаление зависимостей
# ========================================
 
poetry remove requests
poetry remove --group dev pytest
 
# ========================================
# Обновление зависимостей
# ========================================
 
# Обновить все
poetry update
 
# Обновить конкретный пакет
poetry update requests
 
# Показать устаревшие
poetry show --outdated
 
# ========================================
# Просмотр зависимостей
# ========================================
 
# Список всех пакетов
poetry show
 
# Дерево зависимостей
poetry show --tree
 
# Информация о пакете
poetry show requests

Синтаксис версий в Poetry

[tool.poetry.dependencies]
# Каретка (^) - совместимые обновления
requests = "^2.31.0"  # >= 2.31.0, < 3.0.0
django = "^4.2"       # >= 4.2.0, < 5.0.0
 
# Тильда (~) - минимальные обновления
flask = "~2.3.0"      # >= 2.3.0, < 2.4.0
 
# Звёздочка (*) - любая версия
six = "*"             # Latest version
 
# Точная версия
pillow = "10.1.0"     # Только 10.1.0
 
# Диапазон
numpy = ">=1.21,<2.0"
 
# Несколько условий
scipy = ">=1.9,<1.12,!=1.10.0"  # Исключаем 1.10.0
 
# Git репозиторий
my-package = {git = "https://github.com/user/repo.git", branch = "main"}
 
# Локальный путь
my-lib = {path = "../my-lib", develop = true}
 
# С extras
sqlalchemy = {extras = ["asyncio"], version = "^2.0.0"}

Poetry Lock и установка

# ========================================
# Lock файл (poetry.lock)
# ========================================
 
# Создать/обновить lock файл
poetry lock
 
# Lock без обновления версий
poetry lock --no-update
 
# ========================================
# Установка зависимостей
# ========================================
 
# Установить всё (включая dev)
poetry install
 
# Без dev зависимостей (продакшн)
poetry install --without dev
 
# Только конкретные группы
poetry install --only main
poetry install --only dev
 
# С опциональными зависимостями
poetry install --extras "docs mysql"
 
# Синхронизация (удалить лишние пакеты)
poetry install --sync
 
# ========================================
# Экспорт requirements.txt
# ========================================
 
# Зачем это нужно?
# Poetry использует pyproject.toml + poetry.lock (современный формат)
# Но многие инструменты ожидают старый requirements.txt (pip, Docker, etc)
# poetry export конвертирует poetry.lock → requirements.txt
 
# Разбор команды:
# poetry export -f requirements.txt --output requirements.txt
#                │  │                  │        │
#                │  │                  │        └─ Имя файла для записи
#                │  │                  └─ Флаг --output (куда сохранить)
#                │  └─ Формат (requirements.txt, constraints.txt)
#                └─ Флаг -f (сокращение от --format)
 
# Полный экспорт с хешами (для максимальной безопасности)
poetry export -f requirements.txt --output requirements.txt
#             ^                    ^
#             Формат файла         Имя выходного файла
 
# Можно сократить:
poetry export -f requirements.txt -o requirements.txt
#                                  ^^
#                                  -o вместо --output
 
# Или вывести в консоль (без --output):
poetry export -f requirements.txt
# Выведет в stdout, можно перенаправить:
poetry export -f requirements.txt > requirements.txt
 
# Что получится:
# certifi==2023.11.17 \
#     --hash=sha256:xxx... \
#     --hash=sha256:yyy...
# charset-normalizer==3.3.2 \
#     --hash=sha256:zzz...
# requests==2.31.0 \
#     --hash=sha256:aaa...
 
# Только продакшн зависимости (без dev, test, docs)
poetry export -f requirements.txt --output requirements.txt --without dev
 
# Без хешей (короче, совместимее)
poetry export -f requirements.txt --output requirements.txt --without-hashes
 
# Что получится:
# certifi==2023.11.17
# charset-normalizer==3.3.2
# requests==2.31.0
 
# Конкретные группы
poetry export -f requirements.txt --output requirements-dev.txt --only dev
 
# Другие форматы:
poetry export -f constraints.txt -o constraints.txt  # Для pip constraints

Виртуальные окружения

# ========================================
# Управление venv
# ========================================
 
# Создать venv
poetry install  # Автоматически создаёт, если нет
 
# Показать путь к venv
poetry env info --path
 
# Список всех venv для проекта
poetry env list
 
# Удалить venv
poetry env remove python3.11
poetry env remove --all
 
# Использовать конкретную версию Python
poetry env use python3.11
poetry env use /usr/local/bin/python3.11
 
# ========================================
# Запуск команд в venv
# ========================================
 
# Запустить скрипт
poetry run python main.py
poetry run pytest
 
# Открыть shell в venv
poetry shell
 
# Выход из shell
exit  # Или Ctrl+D

Scripts (CLI команды)

Scripts превращают Python функции в консольные команды. Вместо python -m my_project.cli пишите просто my-app. Как npm scripts, но для Python.

Зачем это нужно?

# ❌ Без scripts (неудобно)
poetry run python -m my_project.cli --config prod.yml
poetry run python -m my_project.server --port 8000
 
# ✅ Со scripts (удобно)
poetry run my-app --config prod.yml
poetry run serve --port 8000
 
# Или после poetry shell:
my-app --config prod.yml
serve --port 8000

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

# pyproject.toml
[tool.poetry.scripts]
# Синтаксис: имя-команды = "модуль.путь:функция"
#            │              │           │
#            │              │           └─ Имя функции для вызова
#            │              └─ Путь к модулю (как в import)
#            └─ Имя консольной команды
 
my-app = "my_project.cli:main"
serve = "my_project.server:run"
db-migrate = "my_project.database:migrate"

Реализация Python кода:

# my_project/cli.py
def main():
    """Точка входа для команды my-app"""
 
    print("Hello from my-app!")
    # Здесь может быть argparse, click, typer и т.д.
 
# my_project/server.py
def run():
    """Точка входа для команды serve"""
 
    from uvicorn import run as uvicorn_run
    uvicorn_run("my_project.main:app", host="0.0.0.0", port=8000)
 
# my_project/database.py
def migrate():
    """Точка входа для команды db-migrate"""
 
    print("Running migrations...")
    # Код миграции БД

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

# ========================================
# После poetry install команды доступны
# ========================================
 
# Через poetry run
poetry run my-app
# Hello from my-app!
 
poetry run serve
# Uvicorn running on http://0.0.0.0:8000
 
poetry run db-migrate
# Running migrations...
 
# ========================================
# Или в активированном shell
# ========================================
 
poetry shell
# Теперь команды доступны напрямую:
my-app
serve
db-migrate
 
# ========================================
# С аргументами (передаются в функцию через sys.argv)
# ========================================
 
poetry run my-app --help
poetry run serve --port 3000

Продвинутый пример с Click:

Что такое Click?

Click — популярная библиотека для создания CLI (Command Line Interface) приложений. Это как argparse, но с декораторами и автоматической генерацией help.

Примеры CLI на Click:

  • flask run, flask db migrate — Flask CLI
  • pip install, pip list — pip CLI
  • docker build, docker run — Docker CLI (концептуально похож)

Почему Click?

  • ✅ Декораторы вместо бойлерплейта
  • ✅ Автоматический --help
  • ✅ Валидация типов из коробки
  • ✅ Подкоманды (как git: git commit, git push)
  • ✅ Цветной вывод и прогресс-бары

Пример: создаём CLI приложение с подкомандами

# pyproject.toml
[tool.poetry.dependencies]
python = "^3.9"
click = "^8.1.0"  # Добавляем Click
 
[tool.poetry.scripts]
myapp = "my_project.cli:cli"
#  │                    │
#  │                    └─ Функция cli() (главная группа команд)
#  └─ Будет доступна как команда `myapp`
# my_project/cli.py
import click
 
# ========================================
# Главная группа команд (entry point)
# ========================================
 
@click.group()
def cli():
    """My awesome CLI application
 
    Примеры использования:
      myapp hello --name Alice
      myapp serve --port 3000
    """
    pass
 
# ========================================
# Подкоманда 1: hello
# ========================================
 
@cli.command()  # Добавляем команду в группу cli
@click.option('--name', default='World', help='Name to greet')
#             │         │                │
#             │         │                └─ Описание для --help
#             │         └─ Значение по умолчанию
#             └─ Флаг командной строки
def hello(name):
    """Say hello to someone"""
    click.echo(f'Hello, {name}!')
    #          ^
    #          click.echo вместо print (работает везде)
 
# ========================================
# Подкоманда 2: serve
# ========================================
 
@cli.command()
@click.option('--port', default=8000, type=int, help='Port to run on')
@click.option('--host', default='0.0.0.0', help='Host to bind to')
def serve(port, host):
    """Start the development server"""
    click.echo(f'Starting server on {host}:{port}')
    # Здесь был бы реальный код сервера
    # uvicorn.run(...)
 
# ========================================
# Подкоманда 3: db (с вложенными командами!)
# ========================================
 
@cli.group()
def db():
    """Database management commands"""
    pass
 
@db.command()
def migrate():
    """Run database migrations"""
    click.echo('Running migrations...')
    # Код миграции
 
@db.command()
def seed():
    """Seed database with test data"""
    click.echo('Seeding database...')
    # Код seeding
 
if __name__ == '__main__':
    cli()

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

# ========================================
# После poetry install
# ========================================
 
# Главная справка
poetry run myapp --help
# Usage: myapp [OPTIONS] COMMAND [ARGS]...
#
#   My awesome CLI application
#
#   Примеры использования:
#     myapp hello --name Alice
#     myapp serve --port 3000
#
# Commands:
#   hello  Say hello to someone
#   serve  Start the development server
#   db     Database management commands
 
# ========================================
# Команда hello
# ========================================
 
poetry run myapp hello
# Hello, World!
 
poetry run myapp hello --name Alice
# Hello, Alice!
 
poetry run myapp hello --help
# Usage: myapp hello [OPTIONS]
#   Say hello to someone
# Options:
#   --name TEXT  Name to greet  [default: World]
#   --help       Show this message and exit.
 
# ========================================
# Команда serve
# ========================================
 
poetry run myapp serve
# Starting server on 0.0.0.0:8000
 
poetry run myapp serve --port 3000 --host localhost
# Starting server on localhost:3000
 
# ========================================
# Команды db (вложенные!)
# ========================================
 
poetry run myapp db --help
# Usage: myapp db [OPTIONS] COMMAND [ARGS]...
#   Database management commands
# Commands:
#   migrate  Run database migrations
#   seed     Seed database with test data
 
poetry run myapp db migrate
# Running migrations...
 
poetry run myapp db seed
# Seeding database...

Сравнение с обычным argparse:

# ❌ С argparse (много кода)
import argparse
 
parser = argparse.ArgumentParser(description='My CLI')
subparsers = parser.add_subparsers()
 
hello_parser = subparsers.add_parser('hello')
hello_parser.add_argument('--name', default='World', help='Name to greet')
hello_parser.set_defaults(func=hello)
 
def hello(args):
    print(f'Hello, {args.name}!')
 
args = parser.parse_args()
args.func(args)
 
# ✅ С Click (декораторы!)
import click
 
@click.command()
@click.option('--name', default='World', help='Name to greet')
def hello(name):
    click.echo(f'Hello, {name}!')

Зачем это в разделе Scripts?

Poetry Scripts + Click = мощное комбо для создания CLI инструментов:

[tool.poetry.scripts]
myapp = "my_project.cli:cli"

После poetry install получаете полноценное CLI приложение:

  • Подкоманды (как у git, docker, kubectl)
  • Автоматический --help
  • Валидация типов
  • Удобство использования

Где это используется?

  • 🚀 Веб-приложения: serve для запуска сервера
  • 🗄️ Базы данных: db-migrate, db-seed, db-reset
  • 🧪 Тестирование: test, lint, format
  • 📦 Утилиты: deploy, build, clean
  • 🔧 DevOps: docker-build, k8s-deploy

Packaging и публикация

# ========================================
# Сборка пакета
# ========================================
 
# Собрать wheel и sdist
poetry build
 
# Результат в dist/:
# - my_project-0.1.0-py3-none-any.whl
# - my_project-0.1.0.tar.gz
 
# ========================================
# Публикация в PyPI
# ========================================
 
# Настроить токен (один раз)
poetry config pypi-token.pypi pypi-YOUR_TOKEN_HERE
 
# Опубликовать
poetry publish
 
# Или сборка + публикация
poetry publish --build
 
# ========================================
# Test PyPI (для тестирования)
# ========================================
 
poetry config repositories.testpypi https://test.pypi.org/legacy/
poetry config pypi-token.testpypi pypi-YOUR_TEST_TOKEN
 
poetry publish -r testpypi --build

uv: Скорость на Rust

uv — революция в скорости. Если вам важна производительность CI/CD или работа с большими проектами — это game changer.

Установка uv

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
 
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
 
# Через Homebrew (macOS/Linux)
brew install uv
 
# Через pipx
pipx install uv
 
# Через Cargo (если есть Rust)
cargo install uv
 
# Проверка
uv --version
# uv 0.1.0

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

# ========================================
# Создание проекта
# ========================================
 
# Новый проект с pyproject.toml
uv init my-project
cd my-project
 
# Структура:
# my-project/
#   ├── .python-version    # Версия Python
#   ├── pyproject.toml     # PEP 621 формат
#   └── README.md
 
# ========================================
# Или в существующем проекте
# ========================================
 
cd existing-project
uv init  # Создаст pyproject.toml
 
# ========================================
# Управление Python версиями
# ========================================
 
# Установить Python (uv сам скачивает!)
uv python install 3.12
uv python install 3.11 3.10
 
# Список установленных
uv python list
 
# Использовать конкретную версию
uv python pin 3.12

Управление зависимостями с uv

# ========================================
# Добавление зависимостей
# ========================================
 
# Основные зависимости
uv add requests
uv add "fastapi>=0.100.0"
 
# Dev зависимости
uv add --dev pytest black mypy
 
# С extras
uv add "sqlalchemy[asyncio]"
 
# Git репозиторий
uv add --git https://github.com/user/repo.git
 
# ========================================
# Удаление зависимостей
# ========================================
 
uv remove requests
uv remove --dev pytest
 
# ========================================
# Синхронизация (установка всех зависимостей)
# ========================================
 
# Установить всё из pyproject.toml + создать uv.lock
uv sync
 
# Только продакшн зависимости
uv sync --no-dev
 
# С опциональными группами
uv sync --extra docs
 
# ========================================
# Запуск команд
# ========================================
 
# Запустить Python в venv проекта
uv run python script.py
 
# Запустить приложение
uv run uvicorn main:app
 
# Запустить тесты
uv run pytest
 
# ========================================
# Lock файл
# ========================================
 
# Обновить uv.lock
uv lock
 
# Обновить конкретный пакет
uv lock --upgrade-package requests

uv как замена pip

# ========================================
# Прямая замена pip команд
# ========================================
 
# pip install requests
uv pip install requests
 
# pip install -r requirements.txt
uv pip install -r requirements.txt
 
# pip install -e .
uv pip install -e .
 
# pip freeze
uv pip freeze
 
# pip list
uv pip list
 
# pip show requests
uv pip show requests
 
# ========================================
# Но быстрее! Сравнение скорости
# ========================================
 
# pip install pandas numpy scipy scikit-learn
# Время: ~45 секунд
 
# uv pip install pandas numpy scipy scikit-learn
# Время: ~3 секунды (15x быстрее!)

Структура pyproject.toml для uv

[project]
name = "my-project"
version = "0.1.0"
description = "Fast Python project"
authors = [
    {name = "Your Name", email = "you@example.com"}
]
readme = "README.md"
requires-python = ">=3.9"
license = {text = "MIT"}
 
# Основные зависимости
dependencies = [
    "requests>=2.31.0",
    "fastapi>=0.104.0",
]
 
# Опциональные зависимости
[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "black>=23.11.0",
    "mypy>=1.7.0",
]
docs = [
    "sphinx>=7.2.0",
    "sphinx-rtd-theme>=2.0.0",
]
 
# CLI команды
[project.scripts]
my-app = "my_project.cli:main"
 
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
 
# uv специфичные настройки
[tool.uv]
dev-dependencies = [
    "ruff>=0.1.6",
]

uv + pip-compile workflow

# ========================================
# Генерация requirements.txt с точными версиями
# ========================================
 
# Из pyproject.toml
uv pip compile pyproject.toml -o requirements.txt
 
# Из requirements.in (pip-tools style)
echo "requests" > requirements.in
echo "fastapi>=0.100.0" >> requirements.in
 
uv pip compile requirements.in -o requirements.txt
 
# С опциональными зависимостями
uv pip compile pyproject.toml --extra dev -o requirements-dev.txt
 
# Обновить конкретный пакет
uv pip compile requirements.in -P requests --upgrade-package requests
 
# ========================================
# Установка из скомпилированного файла
# ========================================
 
uv pip sync requirements.txt  # Точная установка (удалит лишнее)

Сравнение Poetry vs uv

Выбор между Poetry и uv — это выбор между полнотой функций и скоростью. Иногда ответ: "оба" (uv для установки, Poetry для публикации).

Сравнительная таблица

ХарактеристикаPoetryuvpip
Скорость установкиСредняя (~20-30s)Очень высокая (~2-3s)Низкая (~45s)
Lock файл✅ poetry.lock✅ uv.lock
Управление venv✅ Встроенное✅ Встроенное❌ Ручное
Dependency resolution✅ Да✅ Да⚠️ Базовое
Dev/Prod separation✅ Группы✅ optional-deps
Building/Publishing✅ Встроенное⚠️ Базовое
Монорепозитории⚠️ Плагины✅ Workspaces
Совместимостьpyproject.tomlPEP 621 + piprequirements
Язык реализацииPythonRustPython
Размер установки~50MB~15MB~10MB
Кривая обученияСредняяНизкаяНизкая
ЭкосистемаЗрелаяРастущаяУстаревшая

Когда использовать Poetry

Используйте Poetry когда:

  • Разрабатываете библиотеку для публикации в PyPI
  • Нужен полный lifecycle management (dev → build → publish)
  • Работаете над приложением с четкой структурой
  • Команда уже использует Poetry
  • Важна стабильность и проверенный инструмент

Пример use case:

# Django/FastAPI приложение с packaging
poetry new my-web-service
poetry add fastapi uvicorn sqlalchemy
poetry add --group dev pytest black mypy
poetry build  # Собираем wheel
poetry publish  # Публикуем в корпоративный PyPI

Когда использовать uv

Используйте uv когда:

  • Скорость CI/CD критична (Docker builds, тесты)
  • Большие проекты с сотнями зависимостей
  • Монорепозиторий с несколькими пакетами
  • Нужна совместимость с pip/pip-tools
  • Хотите современный инструмент на Rust

Пример use case:

# Data Science проект с быстрой установкой
uv init ml-project
uv add pandas numpy scikit-learn matplotlib
uv add --dev jupyter pytest
uv sync  # Установка за секунды!
uv run jupyter notebook

Гибридный подход

# Используем оба инструмента
 
# 1. Poetry для разработки и packaging
poetry init
poetry add requests fastapi
poetry build
 
# 2. uv для быстрой установки в CI/CD
# В Docker/CI используем uv для скорости
RUN uv pip install -r requirements.txt  # 10x быстрее
 
# 3. Генерация requirements.txt через Poetry для uv
poetry export -f requirements.txt -o requirements.txt --without-hashes

Практический пример: как это работает

# ========================================
# Сценарий: У вас есть Poetry проект
# ========================================
 
# pyproject.toml содержит:
[tool.poetry.dependencies]
requests = "^2.31.0"
 
# poetry.lock содержит точные версии:
# requests==2.31.0
# urllib3==2.1.0
# certifi==2023.11.17
# charset-normalizer==3.3.2
# idna==3.6
 
# ========================================
# Шаг 1: Экспорт в requirements.txt
# ========================================
 
poetry export -f requirements.txt -o requirements.txt --without-hashes
 
# Создаёт requirements.txt:
# certifi==2023.11.17
# charset-normalizer==3.3.2
# idna==3.6
# requests==2.31.0
# urllib3==2.1.0
 
# ========================================
# Шаг 2: Используйте в Docker с uv (БЫСТРО!)
# ========================================
 
# Dockerfile
FROM python:3.11-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY requirements.txt .
RUN uv pip install -r requirements.txt --system
# ↑ Установка за 3 секунды вместо 30!
 
# ========================================
# Или в CI/CD
# ========================================
 
# .github/workflows/ci.yml
- name: Install dependencies
  run: |
    curl -LsSf https://astral.sh/uv/install.sh | sh
    uv pip install -r requirements.txt
# ↑ CI проходит в 10 раз быстрее!
 
# ========================================
# Почему это работает?
# ========================================
 
# Poetry Lock (poetry.lock) →
#   poetry export →
#     requirements.txt с точными версиями →
#       uv pip install (FAST!) →
#         идентичное окружение
 
# Вы получаете:
# ✅ Удобство разработки (Poetry)
# ✅ Скорость установки (uv)
# ✅ Воспроизводимость (lock файл)

Миграция с pip/requirements.txt

Миграция — это не "заменить pip на poetry". Это переосмысление структуры проекта. Потратьте время на правильное разделение dev/prod зависимостей.

Миграция на Poetry

# ========================================
# Шаг 1: Установка Poetry
# ========================================
 
curl -sSL https://install.python-poetry.org | python3 -
 
# ========================================
# Шаг 2: Инициализация в существующем проекте
# ========================================
 
cd my-existing-project
poetry init  # Интерактивный мастер
 
# Или автоматически из requirements.txt
poetry init --no-interaction
 
# ========================================
# Шаг 3: Импорт зависимостей из requirements.txt
# ========================================
 
# Если есть requirements.txt
cat requirements.txt | grep -v "^#" | xargs -I {} poetry add {}
 
# Или вручную редактируем pyproject.toml и:
poetry lock
poetry install
 
# ========================================
# Шаг 4: Разделение dev/prod
# ========================================
 
# Переместить dev зависимости
poetry add --group dev pytest black mypy flake8
 
# Удалить из основных (если они там были)
poetry remove pytest black mypy flake8
 
# ========================================
# Шаг 5: Создание lock файла
# ========================================
 
poetry lock
poetry install
 
# ========================================
# Шаг 6: Обновление CI/CD
# ========================================
 
# Старый .gitlab-ci.yml / .github/workflows
pip install -r requirements.txt
pip install -r requirements-dev.txt
 
# Новый
pip install poetry
poetry install --without dev  # Для продакшн
poetry install  # Для тестов

Миграция на uv

# ========================================
# Шаг 1: Установка uv
# ========================================
 
curl -LsSf https://astral.sh/uv/install.sh | sh
 
# ========================================
# Шаг 2: Инициализация
# ========================================
 
cd my-existing-project
uv init
 
# ========================================
# Шаг 3: Импорт из requirements.txt
# ========================================
 
# Автоматическая конвертация
uv add $(cat requirements.txt | grep -v "^#" | grep -v "^-" | cut -d'=' -f1)
 
# Или использовать requirements.txt напрямую
uv pip install -r requirements.txt
 
# Сгенерировать uv.lock из существующих зависимостей
uv lock
 
# ========================================
# Шаг 4: Создание pyproject.toml (PEP 621)
# ========================================
 
# Вручную редактируем pyproject.toml
# или используем существующий
 
# ========================================
# Шаг 5: Обновление CI/CD
# ========================================
 
# Старый
pip install -r requirements.txt
 
# Новый (ЗНАЧИТЕЛЬНО быстрее!)
uv pip install -r requirements.txt
# Или
uv sync --no-dev

Сравнение производительности миграции

# ========================================
# Benchmark: установка Django проекта
# ========================================
 
# requirements.txt (80 зависимостей)
time pip install -r requirements.txt
# real: 1m 23s
 
time poetry install
# real: 0m 42s (2x быстрее pip)
 
time uv pip install -r requirements.txt
# real: 0m 4s (20x быстрее pip, 10x быстрее poetry!)
 
# ========================================
# Benchmark: Docker build
# ========================================
 
# Dockerfile с pip
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install -r requirements.txt
# Build time: ~5 минут
 
# Dockerfile с uv
FROM python:3.11-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY requirements.txt .
RUN uv pip install -r requirements.txt --system
# Build time: ~30 секунд (10x faster!)

Продвинутые техники

Профи-уровень: управление монорепо, private PyPI, зеркала, оптимизация CI/CD. Эти техники отделяют senior разработчиков от middle.

Poetry: Монорепозиторий (Workspaces)

# Структура монорепо
monorepo/
├── packages/
   ├── core/           # Общие утилиты
   ├── pyproject.toml
   └── core/
   ├── api/            # API сервис
   ├── pyproject.toml
   └── api/
   └── worker/         # Background worker
       ├── pyproject.toml
       └── worker/
└── pyproject.toml      # Корневой
# packages/api/pyproject.toml
[tool.poetry]
name = "api"
version = "0.1.0"
 
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.104.0"
# Локальная зависимость
core = {path = "../core", develop = true}
# Установка всех пакетов
cd packages/api
poetry install  # Автоматически установит core
 
# Или используйте poetry-multiproject-plugin
poetry self add poetry-multiproject-plugin
 
# В корневом pyproject.toml
[tool.poetry-multiproject]
projects = [
    "packages/core",
    "packages/api",
    "packages/worker",
]

uv: Workspaces (встроенная поддержка)

# Структура аналогична
monorepo/
├── packages/
   ├── core/
   ├── api/
   └── worker/
└── pyproject.toml
# Корневой pyproject.toml
[tool.uv.workspace]
members = ["packages/*"]
 
# packages/api/pyproject.toml
[project]
name = "api"
dependencies = [
    "fastapi>=0.104.0",
    "core",  # Автоматически находит из workspace
]
# Установка всех workspace пакетов
uv sync
 
# Работа с конкретным пакетом
cd packages/api
uv run uvicorn main:app
 
# Обновление зависимостей во всём workspace
uv lock --upgrade

Private PyPI и зеркала

# ========================================
# Poetry: Private PyPI
# ========================================
 
# Добавить приватный репозиторий
poetry config repositories.private https://pypi.company.com/simple/
 
# Настроить аутентификацию
poetry config http-basic.private username password
 
# Или через переменные окружения
export POETRY_HTTP_BASIC_PRIVATE_USERNAME=username
export POETRY_HTTP_BASIC_PRIVATE_PASSWORD=password
 
# Использование
[tool.poetry.source]
name = "private"
url = "https://pypi.company.com/simple/"
priority = "primary"  # Или "supplemental"
 
[[tool.poetry.source]]
name = "pypi"
priority = "supplemental"  # Fallback на PyPI
 
# Публикация в приватный PyPI
poetry publish -r private
 
# ========================================
# uv: Index URL
# ========================================
 
# Через переменную окружения
export UV_INDEX_URL="https://pypi.company.com/simple/"
 
# Или в pyproject.toml
[tool.uv]
index-url = "https://pypi.company.com/simple/"
extra-index-url = ["https://pypi.org/simple/"]
 
# Или через флаги
uv pip install --index-url https://pypi.company.com/simple/ requests

Кэширование для ускорения CI/CD

# ========================================
# Poetry: GitHub Actions
# ========================================
 
name: CI
on: [push]
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - uses: actions/setup-python@v4
        with:
          python-version: "3.11"
 
      # Кэширование Poetry
      - uses: actions/cache@v3
        with:
          path: |
            ~/.cache/pypoetry
            .venv
          key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}
 
      - name: Install Poetry
        run: pipx install poetry
 
      - name: Install dependencies
        run: poetry install
 
      - name: Run tests
        run: poetry run pytest
# ========================================
# uv: GitHub Actions (БЫСТРЕЕ!)
# ========================================
 
name: CI
on: [push]
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      # uv автоматически кэширует
      - name: Install uv
        run: curl -LsSf https://astral.sh/uv/install.sh | sh
 
      - name: Install dependencies
        run: uv sync
 
      - name: Run tests
        run: uv run pytest
# Ускорение: 5 минут → 30 секунд!
# ========================================
# Poetry: Multi-stage Docker
# ========================================
 
FROM python:3.11-slim as builder
 
RUN pip install poetry
WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.create false \
    && poetry install --only main --no-interaction --no-ansi
 
FROM python:3.11-slim
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . .
CMD ["python", "main.py"]
# ========================================
# uv: Оптимизированный Docker (БЫСТРЕЕ!)
# ========================================
 
FROM python:3.11-slim
 
# Копируем uv binary
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
 
WORKDIR /app
 
# Копируем только зависимости для кэширования слоя
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
 
# Копируем код
COPY . .
 
CMD ["uv", "run", "python", "main.py"]
 
# Build time: 5 минут → 30 секунд!

Оптимизация установки зависимостей

# ========================================
# Poetry: Параллельная установка
# ========================================
 
poetry config installer.parallel true
poetry config installer.max-workers 10
 
# Отключить keyring (ускоряет в CI)
poetry config keyring.enabled false
 
# ========================================
# uv: Уже оптимизирован!
# ========================================
 
# uv по умолчанию использует:
# - Параллельную загрузку
# - Кэширование
# - Zero-copy installations
# - Оптимизацию разрешения зависимостей
 
# Дополнительно: использовать системный Python
uv pip install --system requests  # Не создавать venv

Интеграция с pre-commit

# .pre-commit-config.yaml
 
# ========================================
# С Poetry
# ========================================
 
repos:
  - repo: local
    hooks:
      - id: poetry-check
        name: Poetry check
        entry: poetry check
        language: system
        pass_filenames: false
 
      - id: poetry-lock
        name: Poetry lock check
        entry: poetry lock --check
        language: system
        pass_filenames: false
 
# ========================================
# С uv
# ========================================
 
repos:
  - repo: local
    hooks:
      - id: uv-lock
        name: uv lock check
        entry: uv lock --check
        language: system
        pass_filenames: false

Vendoring зависимостей

Vendoring — это включение копий зависимостей прямо в репозиторий проекта. Нужно для работы без интернета, строгого контроля версий или в закрытых сетях.

Зачем нужен vendoring?

  1. Офлайн установка — работа без доступа к PyPI
  2. Закрытые сети — корпоративные окружения без интернета
  3. Безопасность — контроль всех файлов в репозитории
  4. Воспроизводимость — PyPI может удалить пакет, у вас останется копия
  5. Аудит — проверка всех зависимостей перед использованием

Проблемы pip/PyPI:

# ❌ Обычная установка зависит от PyPI
pip install requests
# Что если:
# - PyPI недоступен?
# - Пакет удалили?
# - Нет интернета?
# - Корпоративный firewall блокирует PyPI?

Решение: Vendoring

# ========================================
# Способ 1: Poetry + pip download (классический)
# ========================================
 
# Шаг 1: Экспорт зависимостей с точными версиями
poetry export -f requirements.txt -o requirements.txt
# Создаётся requirements.txt с хешами для безопасности
 
# Шаг 2: Скачать все wheels в директорию vendor/
pip download -r requirements.txt -d vendor/
# Или с uv (быстрее!):
uv pip download -r requirements.txt -d vendor/
 
# Что скачалось:
# vendor/
#   ├── requests-2.31.0-py3-none-any.whl
#   ├── urllib3-2.1.0-py3-none-any.whl
#   ├── certifi-2023.11.17-py3-none-any.whl
#   ├── charset_normalizer-3.3.2-py3-none-any.whl
#   └── idna-3.6-py3-none-any.whl
 
# Шаг 3: Коммит в git
git add vendor/
git commit -m "Vendor dependencies"
 
# Шаг 4: Установка офлайн (без интернета!)
pip install --no-index --find-links=vendor/ -r requirements.txt
#            │            │                  │
#            │            │                  └─ Список пакетов
#            │            └─ Где искать wheels
#            └─ Не ходить в интернет (только локально)
 
# ========================================
# Способ 2: uv с встроенным кэшем (современный)
# ========================================
 
# Шаг 1: Скачать всё в локальный кэш uv
uv pip download -r requirements.txt
# По умолчанию сохраняется в ~/.cache/uv/
 
# Или в конкретную директорию
uv pip download -r requirements.txt --dest vendor/
 
# Шаг 2: Установка из кэша (офлайн режим)
uv pip install -r requirements.txt --offline
#                                  │
#                                  └─ Использовать только кэш, не ходить в сеть
 
# ========================================
# Способ 3: Poetry с vendor (продвинутый)
# ========================================
 
# Создаём структуру проекта с vendor
my-project/
├── vendor/              # Скачанные зависимости
   ├── requests-2.31.0-py3-none-any.whl
   └── ...
├── pyproject.toml
├── poetry.lock
└── install-offline.sh   # Скрипт офлайн установки
 
# install-offline.sh
#!/bin/bash
set -e
 
# Создать venv
python -m venv .venv
source .venv/bin/activate
 
# Установить Poetry (если нужно)
pip install --no-index --find-links=vendor/ poetry
 
# Установить зависимости из vendor/
pip install --no-index --find-links=vendor/ -r requirements.txt
 
echo "✅ Installed offline from vendor/"

Практический пример: Deployment в закрытой сети

# ========================================
# На машине с интернетом (подготовка)
# ========================================
 
# 1. Экспортируем зависимости
poetry export -f requirements.txt -o requirements.txt --without-hashes
 
# 2. Скачиваем все wheels
uv pip download -r requirements.txt -d vendor/
 
# 3. Создаём архив для переноса
tar -czf dependencies.tar.gz vendor/ requirements.txt
 
# 4. Переносим на закрытую машину (USB, scp, etc)
scp dependencies.tar.gz user@prod-server:/opt/myapp/
 
# ========================================
# На закрытой машине без интернета
# ========================================
 
# 1. Распаковываем
cd /opt/myapp
tar -xzf dependencies.tar.gz
 
# 2. Устанавливаем офлайн
python -m venv .venv
source .venv/bin/activate
pip install --no-index --find-links=vendor/ -r requirements.txt
 
# 3. Запускаем приложение
python main.py
# ✅ Работает без интернета!

Vendoring в Docker (best practice)

# ========================================
# Multi-stage build с vendoring
# ========================================
 
# Стадия 1: Скачивание зависимостей (с интернетом)
FROM python:3.11-slim as builder
 
WORKDIR /app
 
# Копируем только файлы зависимостей
COPY pyproject.toml poetry.lock ./
 
# Устанавливаем Poetry и экспортируем
RUN pip install poetry && \
    poetry export -f requirements.txt -o requirements.txt --without-hashes
 
# Скачиваем все wheels
RUN pip download -r requirements.txt -d /wheels
 
# Стадия 2: Production образ (может быть офлайн)
FROM python:3.11-slim
 
WORKDIR /app
 
# Копируем wheels из builder
COPY --from=builder /wheels /wheels
COPY --from=builder /app/requirements.txt .
 
# Установка БЕЗ доступа к PyPI
RUN pip install --no-index --find-links=/wheels -r requirements.txt && \
    rm -rf /wheels  # Удаляем wheels после установки
 
# Копируем код приложения
COPY . .
 
CMD ["python", "main.py"]
 
# Преимущества:
# ✅ Можно собрать образ без интернета (если есть кэш)
# ✅ Быстрее (не ждём PyPI)
# ✅ Надёжнее (не зависим от доступности PyPI)

Управление размером vendor/

# ========================================
# Проблема: vendor/ может быть огромным
# ========================================
 
# Скачать только для конкретной платформы
pip download \
  -r requirements.txt \
  -d vendor/ \
  --platform manylinux2014_x86_64 \
  --python-version 311 \
  --only-binary=:all:
# Скачает только wheels для Linux x86_64 и Python 3.11
 
# Или для macOS
pip download \
  -r requirements.txt \
  -d vendor/ \
  --platform macosx_11_0_arm64 \
  --python-version 311 \
  --only-binary=:all:
 
# ========================================
# Использование .wheelhouse вместо vendor/
# ========================================
 
# В некоторых проектах используют имя .wheelhouse
pip download -r requirements.txt -d .wheelhouse/
 
# Добавить в .gitignore большие бинарные пакеты
echo ".wheelhouse/numpy*" >> .gitignore
echo ".wheelhouse/torch*" >> .gitignore
# Скачивать их отдельно при deployment

Альтернатива: Private PyPI

Если vendoring не подходит (слишком большой размер), используйте private PyPI:

# ========================================
# Вместо vendoring: поднять свой PyPI
# ========================================
 
# Используйте devpi, pypiserver или JFrog Artifactory
# Загрузите туда все нужные пакеты
 
# Установка из private PyPI
pip install --index-url https://pypi.company.local/simple/ requests
 
# Или с Poetry
poetry config repositories.company https://pypi.company.local/simple/
poetry add --source company requests

Плюсы и минусы vendoring

Плюсы:

  • Полная автономность (работает офлайн)
  • Контроль всех зависимостей
  • Быстрая установка (нет сетевых задержек)
  • Не зависит от доступности PyPI
  • Аудит безопасности (все файлы в репозитории)

Минусы:

  • Большой размер репозитория (особенно с бинарными пакетами)
  • Сложнее обновлять зависимости
  • Нужно vendor для каждой платформы отдельно
  • Git не очень хорош для хранения бинарных файлов

Когда использовать vendoring:

  • 🏢 Корпоративные закрытые сети без интернета
  • 🛡️ Критичные системы (банки, военные, медицина)
  • ✈️ Edge computing (устройства без постоянного интернета)
  • 📦 Дистрибуция приложения (всё в одном архиве)
  • 🔒 Строгий контроль зависимостей (compliance требования)

Best Practices для Production

Production-ready значит: воспроизводимые сборки, быстрый CI/CD, понятные ошибки и zero downtime. Эти практики — не опция, а необходимость.

Чек-лист для production проекта

# ========================================
# 1. Всегда коммитьте lock файл
# ========================================
 
git add poetry.lock    # Poetry
git add uv.lock        # uv
git commit -m "Lock dependencies"
 
# ❌ НЕ добавляйте в .gitignore!
 
# ========================================
# 2. Разделяйте dev и prod зависимости
# ========================================
 
# Poetry
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
 
# uv
[project.optional-dependencies]
dev = ["pytest>=7.4.0"]
 
# ========================================
# 3. Закрепляйте критичные версии
# ========================================
 
# Для библиотек с breaking changes
django = "4.2.7"  # Точная версия
celery = "~5.3.0"  # Только patch updates
 
# ========================================
# 4. Используйте .python-version
# ========================================
 
echo "3.11" > .python-version
 
# Poetry и uv автоматически подхватят
 
# ========================================
# 5. Настройте CI для проверки lock файла
# ========================================
 
# Poetry
poetry lock --check
 
# uv
uv lock --check
 
# ========================================
# 6. Сканирование уязвимостей
# ========================================
 
# Poetry + Safety
poetry export -f requirements.txt | safety check --stdin
 
# uv + pip-audit
uv pip list --format=json | pip-audit --stdin
 
# Или используйте Snyk/Dependabot

Структура проекта для production

my-production-app/
├── .github/
│   └── workflows/
│       ├── ci.yml              # Тесты, линтеры
│       └── cd.yml              # Деплой
├── src/
│   └── my_app/
│       ├── __init__.py
│       ├── main.py
│       ├── config.py           # Конфигурация (env vars)
│       └── ...
├── tests/
│   ├── unit/
│   ├── integration/
│   └── conftest.py
├── docs/
│   └── ...
├── .python-version             # 3.11
├── pyproject.toml              # Зависимости + метаданные
├── poetry.lock / uv.lock       # Lock файл (ВСЕГДА коммитить!)
├── .pre-commit-config.yaml     # Pre-commit hooks
├── Dockerfile                  # Production образ
├── docker-compose.yml          # Локальная разработка
├── .env.example                # Пример переменных окружения
├── Makefile                    # Shortcuts (make install, make test)
└── README.md

Makefile для удобства

Makefile — это файл с короткими командами-алиасами для часто используемых операций. Вместо poetry run pytest -v --cov пишете просто make test.

Что такое Makefile?

Makefile изначально создан для сборки C/C++ проектов, но отлично подходит для автоматизации любых задач:

# ❌ Без Makefile (приходится помнить длинные команды)
poetry run pytest -v --cov=src --cov-report=html
poetry run black . && poetry run ruff check --fix .
poetry run mypy src/ --strict
 
# ✅ С Makefile (короткие алиасы)
make test
make format
make lint

Как создать Makefile:

# Создайте файл с именем Makefile (без расширения!)
touch Makefile
 
# Или
nano Makefile
# vim Makefile
# code Makefile

Как установить make:

# macOS (обычно уже установлен)
xcode-select --install
 
# Или через Homebrew
brew install make
 
# Ubuntu/Debian
sudo apt install make
 
# Windows (через Chocolatey)
choco install make
 
# Или WSL/Git Bash
# make обычно идёт в комплекте
 
# Проверка
make --version
# GNU Make 4.3

Базовая структура Makefile:

# Makefile (с комментариями)
 
# ========================================
# .PHONY — это специальная директива
# ========================================
# Говорит make, что это не файлы, а команды
# Без .PHONY, если у вас есть файл "test", команда "make test" не сработает
.PHONY: install test lint format clean help
 
# ========================================
# Цель (target): зависимости
# 	команда (ВАЖНО: отступ TAB, не пробелы!)
# ========================================
 
# Цель по умолчанию (запускается при "make" без аргументов)
help:
	@echo "Доступные команды:"
	@echo "  make install  - Установить зависимости"
	@echo "  make test     - Запустить тесты"
	@echo "  make lint     - Проверить код линтерами"
	@echo "  make format   - Отформатировать код"
	@echo "  make clean    - Очистить кэш и временные файлы"
	@echo "  make dev      - Запустить dev сервер"
	@echo "  make build    - Собрать проект"
 
# ========================================
# Установка зависимостей
# ========================================
 
install:
	poetry install
	@echo "✅ Dependencies installed"
 
# ========================================
# Тестирование
# ========================================
 
test:
	poetry run pytest -v
 
# Тесты с покрытием
test-cov:
	poetry run pytest -v --cov=src --cov-report=html
	@echo "📊 Coverage report: htmlcov/index.html"
 
# Тесты в watch режиме (для разработки)
test-watch:
	poetry run pytest-watch -v
 
# ========================================
# Линтинг и проверка типов
# ========================================
 
lint:
	@echo "🔍 Running ruff..."
	poetry run ruff check .
	@echo "🔍 Running mypy..."
	poetry run mypy .
	@echo "✅ Lint passed"
 
# ========================================
# Форматирование кода
# ========================================
 
format:
	@echo "✨ Formatting with black..."
	poetry run black .
	@echo "✨ Fixing with ruff..."
	poetry run ruff check --fix .
	@echo "✅ Code formatted"
 
# ========================================
# Очистка временных файлов
# ========================================
 
clean:
	@echo "🧹 Cleaning cache..."
	find . -type d -name __pycache__ -exec rm -rf {} +
	find . -type f -name "*.pyc" -delete
	rm -rf .pytest_cache
	rm -rf .mypy_cache
	rm -rf .ruff_cache
	rm -rf htmlcov
	rm -rf dist
	rm -rf build
	rm -rf *.egg-info
	@echo "✅ Cleaned"
 
# ========================================
# Разработка
# ========================================
 
dev:
	poetry run uvicorn main:app --reload --port 8000
 
# ========================================
# Сборка и публикация
# ========================================
 
build:
	poetry build
	@echo "✅ Built: dist/"
 
publish: build
	poetry publish
	@echo "✅ Published to PyPI"
 
# ========================================
# Цели с зависимостями
# ========================================
 
# "ci" зависит от "lint" и "test"
# Сначала выполнится lint, потом test
ci: lint test
	@echo "✅ CI checks passed"
 
# Полная проверка перед коммитом
pre-commit: format lint test
	@echo "✅ Ready to commit"

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

# ========================================
# Запуск команд
# ========================================
 
# Помощь (команда по умолчанию)
make
# или
make help
 
# Установить зависимости
make install
 
# Запустить тесты
make test
 
# Тесты с покрытием
make test-cov
 
# Линтинг
make lint
 
# Форматирование
make format
 
# Очистка
make clean
 
# Dev сервер
make dev
 
# Сборка
make build
 
# CI проверка (lint + test)
make ci
 
# Перед коммитом (format + lint + test)
make pre-commit
 
# ========================================
# Несколько команд подряд
# ========================================
 
make clean install test
# Выполнит: clean, потом install, потом test

Продвинутый Makefile с переменными:

# Makefile с переменными
 
# ========================================
# Переменные
# ========================================
 
# Инструмент управления зависимостями (poetry или uv)
PKG_MANAGER := poetry
# PKG_MANAGER := uv
 
# Python команда
PYTHON := $(PKG_MANAGER) run python
PYTEST := $(PKG_MANAGER) run pytest
BLACK := $(PKG_MANAGER) run black
RUFF := $(PKG_MANAGER) run ruff
MYPY := $(PKG_MANAGER) run mypy
 
# Директории
SRC_DIR := src
TEST_DIR := tests
 
.PHONY: install test lint format clean help
 
help:
	@echo "Using: $(PKG_MANAGER)"
	@echo "Commands:"
	@echo "  make install  - Install dependencies"
	@echo "  make test     - Run tests"
	@echo "  make lint     - Run linters"
	@echo "  make format   - Format code"
 
install:
ifeq ($(PKG_MANAGER), poetry)
	poetry install
else
	uv sync
endif
 
test:
	$(PYTEST) -v
 
lint:
	$(RUFF) check $(SRC_DIR)
	$(MYPY) $(SRC_DIR)
 
format:
	$(BLACK) $(SRC_DIR) $(TEST_DIR)
	$(RUFF) check --fix $(SRC_DIR)
 
clean:
	find . -type d -name __pycache__ -exec rm -rf {} +
	find . -type f -name "*.pyc" -delete
	rm -rf .pytest_cache .mypy_cache .ruff_cache htmlcov

Makefile для uv проекта:

# Makefile для uv
 
.PHONY: install test lint format clean dev build help
 
help:
	@echo "uv project shortcuts"
	@echo "  make install  - Install dependencies (uv sync)"
	@echo "  make test     - Run tests"
	@echo "  make lint     - Run linters"
	@echo "  make format   - Format code"
	@echo "  make dev      - Start dev server"
 
install:
	uv sync
	@echo "✅ Dependencies synced"
 
test:
	uv run pytest -v --cov
 
lint:
	uv run ruff check .
	uv run mypy .
 
format:
	uv run black .
	uv run ruff check --fix .
 
clean:
	find . -type d -name __pycache__ -exec rm -rf {} +
	rm -rf .pytest_cache .mypy_cache htmlcov
 
dev:
	uv run uvicorn main:app --reload
 
build:
	uv build

Makefile для Docker проекта:

# Makefile с Docker
 
.PHONY: docker-build docker-run docker-test docker-clean
 
# ========================================
# Docker команды
# ========================================
 
docker-build:
	docker build -t myapp:latest .
	@echo "✅ Docker image built: myapp:latest"
 
docker-run:
	docker run -p 8000:8000 myapp:latest
 
docker-test:
	docker run --rm myapp:latest pytest
 
docker-clean:
	docker rmi myapp:latest
	docker system prune -f
 
# ========================================
# Docker Compose
# ========================================
 
up:
	docker compose up -d
	@echo "✅ Services started"
 
down:
	docker compose down
	@echo "✅ Services stopped"
 
logs:
	docker compose logs -f
 
restart: down up

Полезные трюки:

# ========================================
# 1. Тихий вывод (@ подавляет echo команды)
# ========================================
 
# Без @
test:
	poetry run pytest
 
# Вывод:
# poetry run pytest
# ===== test session starts =====
 
# С @
test:
	@poetry run pytest
 
# Вывод:
# ===== test session starts =====
 
# ========================================
# 2. Условия
# ========================================
 
install:
ifeq ($(shell which poetry),)
	@echo "❌ Poetry not found, installing..."
	pip install poetry
endif
	poetry install
 
# ========================================
# 3. Цепочки зависимостей
# ========================================
 
# "deploy" зависит от "test", который зависит от "lint"
lint:
	@echo "Linting..."
 
test: lint
	@echo "Testing..."
 
deploy: test
	@echo "Deploying..."
 
# make deploy выполнит: lint → test → deploy
 
# ========================================
# 4. Многострочные команды
# ========================================
 
setup:
	@echo "Setting up project..."
	python -m venv .venv
	. .venv/bin/activate && \
		pip install poetry && \
		poetry install
	@echo "✅ Setup complete"
 
# ========================================
# 5. Использование shell команд
# ========================================
 
check-python:
	@python --version
	@echo "Python location: $(shell which python)"

Частые ошибки:

# ❌ ОШИБКА 1: Пробелы вместо TAB
install:
    poetry install  # Используются пробелы!
# Ошибка: Makefile:2: *** missing separator.  Stop.
 
# ✅ Правильно: TAB (не пробелы!)
install:
	poetry install  # Используется TAB!
 
# ❌ ОШИБКА 2: Забыли .PHONY
test:
	pytest
# Если есть файл "test", команда не выполнится
 
# ✅ Правильно:
.PHONY: test
test:
	pytest
 
# ❌ ОШИБКА 3: Опечатка в имени цели
make tset  # вместо test
# Ошибка: make: *** No rule to make target `tset'.  Stop.

Реальный пример production Makefile:

# Production Makefile
 
PROJECT := myapp
PYTHON_VERSION := 3.11
 
.PHONY: install dev test lint format clean build deploy help
 
.DEFAULT_GOAL := help
 
help:
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
 
install: ## Install dependencies
	poetry install --no-root
 
dev: ## Start development server
	poetry run uvicorn $(PROJECT).main:app --reload --port 8000
 
test: ## Run tests
	poetry run pytest -v --cov=$(PROJECT) --cov-report=term-missing
 
lint: ## Run linters
	poetry run ruff check $(PROJECT)
	poetry run mypy $(PROJECT)
 
format: ## Format code
	poetry run black $(PROJECT) tests
	poetry run ruff check --fix $(PROJECT)
 
clean: ## Clean cache and temporary files
	find . -type d -name __pycache__ -exec rm -rf {} +
	rm -rf .pytest_cache .mypy_cache htmlcov dist
 
build: clean test ## Build distribution
	poetry build
 
deploy: build ## Deploy to production
	@echo "Deploying to production..."
	./deploy.sh
 
ci: lint test ## Run CI checks
	@echo "✅ CI passed"

Теперь используя make help вы увидите красивое меню! 🎨

Версионирование и changelog

# ========================================
# Poetry: Semantic versioning
# ========================================
 
# Обновление версии
poetry version patch   # 0.1.0 → 0.1.1
poetry version minor   # 0.1.1 → 0.2.0
poetry version major   # 0.2.0 → 1.0.0
 
# Или вручную
poetry version 1.2.3
 
# ========================================
# Автоматическая генерация CHANGELOG
# ========================================
 
# Используйте conventional commits
git commit -m "feat: add user authentication"
git commit -m "fix: resolve login bug"
 
# Инструменты для changelog
pip install commitizen
cz bump --changelog

Частые проблемы и решения

95% проблем с зависимостями — это конфликты версий или забытый lock файл. Остальные 5% —

.

Poetry: Типичные проблемы

# ========================================
# Проблема 1: Медленное разрешение зависимостей
# ========================================
 
# Симптом: poetry lock висит часами
poetry lock --no-update  # Не обновлять версии
 
# Или очистить кэш
poetry cache clear pypi --all
 
# ========================================
# Проблема 2: Конфликт версий
# ========================================
 
# Ошибка: "package X requires Y, but Z requires Y>=2"
 
# Решение: посмотреть дерево зависимостей
poetry show --tree
 
# Найти конфликт
poetry show package-name
 
# Закрепить версию вручную
[tool.poetry.dependencies]
problematic-lib = "1.2.3"  # Точная версия
 
# ========================================
# Проблема 3: Poetry не находит Python
# ========================================
 
# Указать путь явно
poetry env use /usr/local/bin/python3.11
 
# Или через pyenv
poetry env use $(pyenv which python)
 
# ========================================
# Проблема 4: Ошибка keyring
# ========================================
 
# Отключить keyring
poetry config keyring.enabled false

uv: Типичные проблемы

# ========================================
# Проблема 1: Конфликт с pip
# ========================================
 
# Не смешивайте pip и uv в одном venv
# Или используйте --system для системных пакетов
 
# ========================================
# Проблема 2: uv не находит Python
# ========================================
 
# Установить Python через uv
uv python install 3.11
 
# Или указать путь
uv venv --python /usr/local/bin/python3.11
 
# ========================================
# Проблема 3: Кэш занимает много места
# ========================================
 
# Очистить кэш
uv cache clean
 
# Посмотреть размер кэша
uv cache dir
 
# ========================================
# Проблема 4: Ошибка при компиляции пакетов
# ========================================
 
# Использовать pre-built wheels
uv pip install package-name --only-binary :all:
 
# Или пропустить проблемный пакет
uv pip install --no-build-isolation package-name

Общие проблемы

# ========================================
# Проблема: Разные версии на dev и prod
# ========================================
 
# ❌ Неправильно
pip install requests  # Нет фиксации версии
 
# ✅ Правильно
poetry install  # Использует poetry.lock
uv sync         # Использует uv.lock
 
# ========================================
# Проблема: Медленный CI
# ========================================
 
# ❌ Медленно
pip install -r requirements.txt  # 5 минут
 
# ✅ Быстро
uv pip install -r requirements.txt  # 30 секунд
 
# ✅ Ещё быстрее с кэшированием
- uses: actions/cache@v3
  with:
    path: ~/.cache/uv
    key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }}
 
# ========================================
# Проблема: Большой Docker образ
# ========================================
 
# ❌ Плохо: 1.5GB
FROM python:3.11
COPY requirements.txt .
RUN pip install -r requirements.txt
 
# ✅ Хорошо: 200MB
FROM python:3.11-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY uv.lock pyproject.toml .
RUN uv sync --frozen --no-dev

Заключение

Современное управление зависимостями — это не роскошь, а необходимость. Poetry для полноты, uv для скорости, оба лучше pip. Выбирайте инструмент под задачу, а не под хайп.

Ключевые выводы

  1. pip + requirements.txt устарели — нет lock файлов, нет dependency resolution
  2. Poetry — стандарт де-факто для библиотек и полнофункциональных проектов
  3. uv — революция в скорости, идеален для CI/CD и больших проектов
  4. Lock файлы обязательны — коммитьте poetry.lock / uv.lock
  5. Разделяйте dev/prod — экономия времени и места в production
  6. Используйте pyproject.toml — современный стандарт (PEP 518/621)
  7. Кэшируйте зависимости в CI — сокращение времени сборки в 10+ раз
  8. Монорепо требуют workspaces — Poetry плагины или uv workspaces

Рекомендации по выбору

Выбирайте Poetry если:

  • 📦 Разрабатываете библиотеку для PyPI
  • 🏢 Нужна корпоративная поддержка и стабильность
  • 📚 Команда уже использует Poetry
  • 🔧 Нужен полный packaging workflow

Выбирайте uv если:

  • ⚡ Скорость критична (CI/CD, Docker builds)
  • 🏗️ Большой проект с сотнями зависимостей
  • 🔄 Монорепозиторий с workspaces
  • 🚀 Хотите cutting-edge технологии

Используйте оба если:

  • Poetry для разработки и packaging
  • uv для CI/CD и production установок
  • Экспортируйте requirements.txt из Poetry для uv

Чек-лист миграции

  • Установлен Poetry или uv
  • Создан pyproject.toml с зависимостями
  • Разделены dev/prod зависимости
  • Сгенерирован lock файл (poetry.lock / uv.lock)
  • Lock файл добавлен в git
  • Обновлены CI/CD конфиги
  • Обновлены Dockerfile
  • Настроено кэширование в CI
  • Добавлены pre-commit hooks
  • Команда обучена новым инструментам

Следующие шаги

  1. Попробуйте оба инструмента на тестовом проекте
  2. Измерьте скорость установки зависимостей
  3. Оцените влияние на CI/CD время сборки
  4. Обучите команду новым workflow
  5. Мигрируйте постепенно начиная с новых проектов

Полезные ресурсы

Официальная документация:

Инструменты:

  • Poetry — установка и docs
  • uv — GitHub репозиторий
  • commitizen — semantic versioning
  • pip-audit — сканирование уязвимостей

Сообщество:

Помните: Инструмент — это средство, а не цель. Выбирайте то, что решает ваши проблемы, а не создаёт новые. И да, можно использовать оба — они не конкурируют, а дополняют друг друга.