Poetry и uv: современное управление зависимостями в Python
Полное практическое руководство по Poetry и uv — от базовой установки до продвинутых техник управления зависимостями, монорепозиториями и CI/CD
Table of Contents
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+DScripts (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 CLIpip install,pip list— pip CLIdocker 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 --builduv: Скорость на 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 requestsuv как замена 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 для публикации).
Сравнительная таблица
| Характеристика | Poetry | uv | pip |
|---|---|---|---|
| Скорость установки | Средняя (~20-30s) | Очень высокая (~2-3s) | Низкая (~45s) |
| Lock файл | ✅ poetry.lock | ✅ uv.lock | ❌ |
| Управление venv | ✅ Встроенное | ✅ Встроенное | ❌ Ручное |
| Dependency resolution | ✅ Да | ✅ Да | ⚠️ Базовое |
| Dev/Prod separation | ✅ Группы | ✅ optional-deps | ❌ |
| Building/Publishing | ✅ Встроенное | ⚠️ Базовое | ❌ |
| Монорепозитории | ⚠️ Плагины | ✅ Workspaces | ❌ |
| Совместимость | pyproject.toml | PEP 621 + pip | requirements |
| Язык реализации | Python | Rust | Python |
| Размер установки | ~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 --upgradePrivate 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: falseVendoring зависимостей
Vendoring — это включение копий зависимостей прямо в репозиторий проекта. Нужно для работы без интернета, строгого контроля версий или в закрытых сетях.
Зачем нужен vendoring?
- Офлайн установка — работа без доступа к PyPI
- Закрытые сети — корпоративные окружения без интернета
- Безопасность — контроль всех файлов в репозитории
- Воспроизводимость — PyPI может удалить пакет, у вас останется копия
- Аудит — проверка всех зависимостей перед использованием
Проблемы 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 htmlcovMakefile для 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 buildMakefile для 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 falseuv: Типичные проблемы
# ========================================
# Проблема 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. Выбирайте инструмент под задачу, а не под хайп.
Ключевые выводы
- pip + requirements.txt устарели — нет lock файлов, нет dependency resolution
- Poetry — стандарт де-факто для библиотек и полнофункциональных проектов
- uv — революция в скорости, идеален для CI/CD и больших проектов
- Lock файлы обязательны — коммитьте poetry.lock / uv.lock
- Разделяйте dev/prod — экономия времени и места в production
- Используйте pyproject.toml — современный стандарт (PEP 518/621)
- Кэшируйте зависимости в CI — сокращение времени сборки в 10+ раз
- Монорепо требуют 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
- Команда обучена новым инструментам
Следующие шаги
- Попробуйте оба инструмента на тестовом проекте
- Измерьте скорость установки зависимостей
- Оцените влияние на CI/CD время сборки
- Обучите команду новым workflow
- Мигрируйте постепенно начиная с новых проектов
Полезные ресурсы
Официальная документация:
Инструменты:
- Poetry — установка и docs
- uv — GitHub репозиторий
- commitizen — semantic versioning
- pip-audit — сканирование уязвимостей
Сообщество:
- r/Python — обсуждения Python инструментов
- Poetry Discord — официальный Discord
- Astral Discord — uv и Ruff community
Помните: Инструмент — это средство, а не цель. Выбирайте то, что решает ваши проблемы, а не создаёт новые. И да, можно использовать оба — они не конкурируют, а дополняют друг друга.