1. Введение: «У меня на компьютере всё работало!»
Дисклеймер: Всегда проверяйте скрипты перед исполнением, даже если они загружены с официальных ресурсов. Статья отражает субъективное мнение автора и актуальна на момент написания. Инструменты и версии библиотек могут изменяться.
Знакомая ситуация? Пятница, вечер. Вы дописываете фичу, запускаете тесты — всё зеленое. С чувством выполненного долга пушите код в репозиторий, кидаете ссылку коллеге (или деплоите на тестовый сервер) и идете заваривать чай.
А через пять минут прилетает сообщение: «Слушай, оно падает на старте. Пишет ModuleNotFoundError».
Вы недоумеваете. Как так? Вы же сделали pip freeze > requirements.txt перед коммитом! Вы открываете консоль, снова запускаете скрипт — у вас всё работает идеально. Вы тратите час на разборки и выясняете, что:
Коллега установил библиотеки неделю назад, и у него
requestsверсии 2.25, а у вас 2.31.Или в ваш
requirements.txtслучайно попал какой-нибудь системный пакет, которого нет на сервере.Или вы поставили крутую библиотеку для форматирования даты, но забыли добавить её в список, потому что она была у вас в глобальном окружении.
В этот момент приходит осознание: управление зависимостями в Python сломано.
Мы привыкли использовать pip и venv как стандарт де-факто. Это работает, если ваш скрипт состоит из одного файла и двух библиотек. Но как только проект растет, файл requirements.txt превращается в мину замедленного действия. В нем смешиваются в кучу прямые зависимости (то, что ставили вы) и транзитивные (то, что притянули ваши библиотеки). Разобраться в этой «портянке» через полгода невозможно.
Почему так происходит? Потому что Pip — это установщик пакетов (package installer). Он отлично умеет скачивать файлы с PyPI и класть их в папку. Но он не является менеджером проектов. Он ничего не знает о совместимости версий, о разделении сред разработки и продакшена, и он не гарантирует, что сборка будет воспроизводима.
В других языках (JavaScript с npm, Rust с Cargo, Go с mod) эта проблема решена «из коробки». Хорошая новость: в Python это решение тоже есть.
Встречайте Poetry — инструмент, который заставит вас навсегда забыть о requirements.txt как о страшном сне.
2. Почему requirements.txt — это боль (The Old Way)
Казалось бы, что может быть проще списка названий библиотек в текстовом файле? Но именно эта простота играет злую шутку, когда проект перерастает рамки «Hello World».
Вот три всадника Апокалипсиса, которые приходят вместе с requirements.txt:
1. Свалка транзитивных зависимостей
Допустим, вы начали новый проект и установили FastAPI.
Вы пишете: pip install fastapi.
Затем, по классике, делаете: pip freeze > requirements.txt.
Открываете файл и видите это:
anyio==3.7.1
click==8.1.7
fastapi==0.103.1
h11==0.14.0
idna==3.4
pydantic==2.3.0
sniffio==1.3.0
starlette==0.27.0
typing_extensions==4.7.1
Стоп. Вы устанавливали только FastAPI. Откуда взялись остальные 8 строк? Это транзитивные зависимости — библиотеки, которые нужны вашим библиотекам.
В requirements.txt всё свалено в одну кучу. Через полгода вы откроете этот файл и не вспомните: «Мне правда нужен был click для CLI-команд, или он прилетел вместе с чем-то другим?». А если вы удалите fastapi, эти «сироты» останутся висеть мертвым грузом, засоряя окружение.
2. Дилемма версий: Жестко или Мягко?
У вас есть два пути, и оба — плохие.
-
Путь параноика (Pinning): Вы жестко фиксируете версии (
pandas==1.5.3).Плюс: Стабильность. Сборка всегда одинаковая.
Минус: Вы не получаете обновлений безопасности и багфиксов. Через год ваш проект — это музей древностей с уязвимостями. Обновлять всё руками — адский труд.
-
Путь оптимиста: Вы не фиксируете версии или ставите диапазон (
pandas>=1.5.3).Плюс: Всегда свежие библиотеки.
Минус: В один прекрасный день выходит
pandas 2.0с ломающими изменениями (Breaking Changes). Ваш CI/CD падает, прод лежит, вы в панике ищете, какая библиотека обновилась и сломала совместимость.
requirements.txt не умеет разделять понятия «версия, которую я хочу» и «версия, которая работает сейчас».
3. Костыли для Dev и Prod
Вам нужен pytest и black для разработки, но тащить их в Docker-контейнер на продакшн — плохая практика (лишний вес и векторы атак).
Как это решается «по старинке»? Правильно, созданием зоопарка файлов:
requirements.txt(база)requirements.dev.txt(для разработки:-r requirements.txt+ тесты)requirements.test.txt(для CI)
Вам приходится вручную следить за актуальностью всех этих файлов. Забыли обновить базовый файл? Тесты в dev-окружении прошли, а прод упал.
Итог: Используя pip freeze, мы пытаемся забивать гвозди микроскопом. Нам нужен инструмент, который отделит то, что мы хотим (наши прямые зависимости), от того, что нужно машине (полное дерево версий).
3. Что такое Poetry и файл pyproject.toml
Если pip — это молоток, которым можно забивать гвозди (устанавливать пакеты), то Poetry — это полноценный строительный комбинат.
Poetry позиционирует себя не просто как пакетный менеджер, а как инструмент для управления жизненным циклом проекта. Он берет на себя сразу три задачи, для которых раньше требовались разные утилиты:
Управление зависимостями (замена
pip).Управление виртуальными окружениями (замена
venv/virtualenv). Вам больше не нужно писатьsource venv/bin/activate, Poetry сам знает, где лежит окружение для этого проекта.Сборка и публикация пакетов (замена
setuptoolsиtwine).
Единый источник правды: pyproject.toml
Раньше конфигурация Python-проекта была размазана по куче файлов: зависимости в requirements.txt, настройки сборки в setup.py или setup.cfg, настройки линтеров в .flake8, настройки тестов в pytest.ini.
Сообщество Python решило прекратить этот хаос и приняло стандарт PEP 518. Так появился файл pyproject.toml. Это единый конфигурационный файл для всего проекта.
Poetry использует именно его. Забудьте о бесконечных списках непонятных библиотек. Взгляните, как выглядит зависимость проекта в мире Poetry:
[tool.poetry]
name = "my-super-project"
version = "0.1.0"
description = "Пример проекта на Хабр"
authors = ["Enamored Poc <email@example.com>"]
[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.103.0"
uvicorn = {extras = ["standard"], version = "^0.23.0"}
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
black = "^23.9.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
В чем магия этого файла?
Декларативность: В секции
[tool.poetry.dependencies]вы перечисляете только прямые зависимости — то, что вы реально импортируете в коде (import fastapi). Здесь нет мусора типаidna,h11илиsniffio. Файл остается чистым и читаемым, даже если под капотом у вас сотня библиотек.Умные группы: Обратите внимание на секцию
[tool.poetry.group.dev.dependencies]. Зависимости для разработки (pytest,black) лежат отдельно. При деплое на прод вы просто скажете Poetry: «Установи всё, кроме dev-группы», и он сделает это одной командой. Никаких лишних файловrequirements.dev.txt.Семантическое версионирование: Значок
^(caret) перед версией^0.103.0означает: «Разрешаю обновлять эту библиотеку до любой версии, которая не ломает обратную совместимость (до0.200.0или1.0.0)». Это золотая середина между жесткой фиксацией и полным хаосом.
pyproject.toml — это ваш контракт с проектом. Вы говорите: «Мне нужен FastAPI и Python не ниже 3.10». А как именно это обеспечить — теперь головная боль Poetry, а не ваша.
4. Киллер-фича: Lock-файл (poetry.lock)
Помните проблему из введения, когда код работал у вас, но падал у коллеги? Чаще всего виноваты не прямые зависимости, а транзитивные (зависимости ваших зависимостей), которые обновились без вашего ведома.
Обычный pip устанавливает версии библиотек «на лету». Если вы напишете install flask, он скачает самую свежую версию Flask и самые свежие версии всех библиотек, от которых Flask зависит. Запустите ту же команду через неделю — и получите уже другой набор пакетов, потому что за это время обновился условный Werkzeug.
Poetry решает это радикально. Он разделяет «Список желаний» и «Суровую реальность».
pyproject.toml — это ваш список желаний. Вы говорите: «Я хочу
Djangoверсии 4.2 или новее».poetry.lock — это суровая реальность. Это «снимок» (snapshot) всего дерева зависимостей в конкретный момент времени.
Как это работает?
Когда вы впервые добавляете библиотеку или запускаете poetry install, Poetry делает сложную работу: он резолвит (разрешает) зависимости. Он строит граф всех библиотек, ищет версии, которые совместимы друг с другом, и, найдя идеальную комбинацию, записывает её в файл poetry.lock.
В этом файле (который не стоит править руками) зафиксировано всё:
Точные версии всех пакетов (например,
numpy 1.24.3, а не просто^1.24).Ссылки, откуда скачаны файлы.
Криптографические хеши файлов.
Детерминизм — рай для DevOps
Главное правило работы с Poetry: Lock-файл всегда нужно комитить в репозиторий!
Зачем? Представьте сценарий:
Вы добавили библиотеку на своем ноутбуке. Poetry сгенерировал
lock-файл. Вы запушили изменения.Ваш коллега делает
git pullи запускаетpoetry install.Poetry видит наличие
lock-файла. Он игнорирует процесс подбора версий и просто скачивает пакеты, указанные в локе.
Результат: У коллеги байт-в-байт то же окружение, что и у вас.
Даже если завтра выйдет новая версия библиотеки, ломающая всё на свете, — вашему проекту плевать. Пока вы явно не попросите Poetry обновиться (poetry update), он будет использовать зафиксированные, проверенные версии.
Безопасность бонусом
Помимо стабильности, poetry.lock дает защиту от атак типа Dependency Confusion или подмены пакетов. Благодаря проверке хешей, если злоумышленник подменит пакет на PyPI (или взломает ваш корпоративный репозиторий) и зальет вредоносный код под той же версией, Poetry откажется его устанавливать, потому что контрольная сумма файла не совпадет с той, что в локе.
Итог: С poetry.lock фраза «Но на моем компьютере работает!» теряет смысл. Если работает локально — будет работать и на проде. Это и есть тот самый детерминизм, за который Poetry так любят в продакшене.
5. Практика: Poetry за 5 минут (Tutorial)
Хватит теории, давайте пощупаем инструмент руками. Представим, что мы начинаем новый проект.
Шаг 1: Установка
Самый правильный способ (согласно документации) — через официальный скрипт, чтобы Poetry не смешивался с вашими системными библиотеками.
Linux / macOS:
curl -sSL https://install.python-poetry.org | python3 -
Windows (PowerShell):
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
(Если совсем лень, можно и pip install poetry, но лучше привыкать к хорошему сразу).
Шаг 2: Создание проекта
Poetry умеет создавать структуру папок за вас. Запустите:
poetry new my-super-bot
cd my-super-bot
Внутри вы увидите идеальную чистоту:
my-super-bot/
├── pyproject.toml <-- Наш главный конфиг
├── README.md
├── my_super_bot/ <-- Пакет с кодом
│ └── __init__.py
└── tests/ <-- Тесты
└── __init__.py
Лайфхак: Если у вас уже есть папка с кодом, зайдите в неё и напишите poetry init — мастер настройки задаст пару вопросов и создаст pyproject.toml.
Шаг 3: Добавляем библиотеки
Забудьте про ручное редактирование файлов.
Нам нужен requests для работы и pytest для тестов.
Ставим основную библиотеку:
poetry add requests
Смотрите, что происходит в терминале: Poetry сам находит подходящую версию, разрешает зависимости, создает виртуальное окружение (если его нет) и записывает всё в poetry.lock.
Ставим библиотеку для разработчика:
poetry add pytest --group dev
Ключ --group dev — это та самая магия. Мы явно говорим: «Эта библиотека нужна только мне, на продакшене её не ставить».
Шаг 4: Запуск кода
Новички часто теряются: «Я установил библиотеку, пишу python main.py, а он говорит Module not found!».
Всё потому, что Poetry заботливо спрятал виртуальное окружение, чтобы не захламлять папку проекта.
Есть два пути запуска:
-
Одноразовый (через run):
poetry run python main.pyЭто означает: «Poetry, найди свое окружение и запусти там эту команду».
-
Постоянный (через shell):
poetry shellЭта команда активирует виртуальное окружение в вашем терминале (появится префикс
(my-super-bot-py3.10)). Теперь можно писать простоpython main.py.
Шаг 5: Проверка зависимостей
Хотите почувствовать себя хакером? Посмотрите дерево зависимостей, которое выстроил Poetry:
poetry show --tree
Вывод:
requests 2.31.0 Python HTTP for Humans.
├── certifi >=2017.4.17
├── charset-normalizer >=2,<4
├── idna >=2.5,<4
└── urllib3 >=1.21.1,<3
Сразу видно, кто кого «притащил» в проект.
Шпаргалка для коллеги
Когда ваш коллега скачает этот проект, ему не нужно будет повторять эти шаги. Ему нужно сделать всего одну команду:
poetry install
Poetry прочитает lock-файл и воссоздаст у коллеги абсолютно идентичное окружение.
6. Как переехать с Pip на Poetry (Миграция)
«Звучит круто, но у меня уже есть проект с requirements.txt на 50 строк. Мне что, переписывать всё руками?» — спросите вы.
Вообще-то, да. И нет.
Переезд на Poetry — это лучший повод провести «генеральную уборку» в зависимостях. У вас есть два пути.
Путь 1: «Генеральная уборка» (Рекомендуемый)
Проблема автоматического импорта старого requirements.txt в том, что вы перетащите в новый дом и старый мусор (транзитивные зависимости). Если у вас в файле лежит и pandas, и numpy (который нужен пандасу), и pytz (который тоже нужен пандасу), то Poetry, конечно, это съест. Но файл конфигурации будет грязным.
Алгоритм действий:
Зайдите в папку проекта и сделайте
poetry init.Откройте свой старый
requirements.txtв текстовом редакторе.Прочитайте его глазами. Выберите только те библиотеки, которые вы реально импортируете в своем коде.
-
Добавьте их командой:
poetry add django djangorestframework celery redis Poetry сам подтянет все недостающие под-зависимости, но не будет замусоривать ими ваш главный конфиг.
-
Отдельно добавьте инструменты разработки:
poetry add --group dev pytest flake8
Путь 2: «Мне только спросить» (Быстрый, для Linux/macOS)
Если времени нет, файл огромный, а разбираться, кто от кого зависит, лень — можно скормить старый список целиком.
Используем магию консоли:
cat requirements.txt | xargs poetry add
Эта команда прочитает каждую строчку из файла и передаст её в poetry add.
Внимание: Если в вашем файле зафиксированы версии (==1.2.3), Poetry попытается установить именно их. Если там есть конфликты (которые pip игнорировал), Poetry выдаст ошибку и заставит вас их решать. И это хорошо — лучше узнать о конфликте сейчас, чем ловить баги на проде.
Финальный аккорд
После того как зависимости установлены:
Проверьте работоспособность: Запустите тесты через
poetry run pytest.-
Удалите прошлое:
rm requirements.txt(Или
requirements-dev.txt, если был). Это важный психологический момент — сжечь мосты, чтобы ни у кого не возникло соблазна сделатьpip installпо старой памяти. Закомитьте результат: Добавьте
pyproject.tomlиpoetry.lockв Git.
А как же Docker?
Если вы деплоите через Docker, ваш Dockerfile станет даже проще. Вместо копирования текстовиков вы будете использовать Poetry.
Пример современного Dockerfile:
FROM python:3.11-slim
# Устанавливаем poetry
RUN pip install poetry
WORKDIR /app
COPY pyproject.toml poetry.lock ./
# Ставим зависимости (без создания venv, внутри контейнера он не нужен)
RUN poetry config virtualenvs.create false \
&& poetry install --no-interaction --no-ansi
COPY . .
CMD ["python", "main.py"]
Теперь ваш проект полностью перевезен на новые рельсы.
7. Ложка дегтя (Честный обзор)
Я не буду вам врать: Poetry — не «серебряная пуля». Это сложный инструмент, и, как у любого сложного инструмента, у него есть свои недостатки. Перед тем как переводить на него весь отдел, вы должны знать, с чем можете столкнуться.
1. Скорость (Resolving Dependencies...)
Это главная претензия к Poetry. Так как он написан на Python, а задача разрешения зависимостей (Dependency Resolution) математически сложна, на больших проектах Poetry может «задуматься».
Если вы попытаетесь установить комбинацию из pandas, pytorch, scipy и еще десятка тяжелых библиотек, надпись Resolving dependencies... может висеть минуту, две, а то и пять. Pip в этом плане быстрее, потому что он «глупее»: он не пытается просчитать идеальный граф совместимости, а просто ставит что дают.
2. Где мой Venv?
По умолчанию Poetry создает виртуальные окружения не в папке проекта, а в системном кэше (например, ~/.cache/pypoetry/virtualenvs/).
Для новичков это шок: открываешь папку проекта, а там нет папки .venv. IDE (VS Code, PyCharm) иногда тупят и не подхватывают окружение автоматически.
Лечение: Одна команда, которую стоит выполнить сразу после установки Poetry:
poetry config virtualenvs.in-project true
Теперь папка .venv будет создаваться прямо внутри проекта, как вы привыкли.
3. Война стандартов
Мир Python-пакетирования — это поле битвы. Есть старый setuptools, есть легковесный flit, есть hatch. Poetry использует стандарт pyproject.toml, но формат записи зависимостей внутри него специфичен именно для Poetry.
Это значит, что вы попадаете в Vendor Lock-in: если захотите уйти с Poetry, вам придется переписывать конфиг под другой инструмент. Это не смертельно, но неприятно.
4. Новый игрок на поле: uv
Нельзя писать статью про управление пакетами в 2024-2025 году и не упомянуть uv.
Это новый менеджер пакетов от создателей линтера Ruff. Он написан на Rust.
Poetry: Надежный, стабильный, много фич, но медленный.
uv: Молодой, дерзкий и безумно быстрый.
uv уже умеет работать с форматом Poetry и заменять pip. Стоит ли бросать всё и бежать на uv?
Если у вас гигантский монолит и Poetry тормозит — да, попробуйте.
Если вам важна стабильность, документация и проверенные годами решения — оставайтесь на Poetry. uv пока еще в фазе активного роста, а Poetry — это «золотой стандарт» прямо сейчас.
? Домашнее задание: Закрепляем на практике
Вы прочитали теорию, теперь время «набить руку». Я подготовил 5 задач, которые проведут вас по всему циклу работы с Poetry.
? Нажмите, чтобы открыть список задач (5 шт.)
Задача 1: Инициализация
Создайте новую папку habr-poetry-challenge. Инициализируйте в ней проект в интерактивном режиме (через init), укажите имя проекта challenge и версию Python ^3.10.
(Цель: Получить готовый pyproject.toml)
Задача 2: Добавление зависимостей
Установите библиотеку pendulum (это удобная замена datetime). Убедитесь, что она появилась в секции [tool.poetry.dependencies] вашего toml-файла.
(Цель: Поработать с командой add и увидеть изменения в конфиге)
Задача 3: Разделение окружений
Установите линтер ruff, но так, чтобы он попал в группу разработки (dev).
(Цель: Научиться использовать флаг --group)
Задача 4: Запуск кода
Создайте файл main.py со следующим содержимым:
import pendulum
now = pendulum.now("Europe/Moscow")
print(f"Привет, Хабр! Сейчас: {now.to_datetime_string()}")
Запустите этот скрипт с помощью Poetry, не активируя оболочку (shell) вручную.
(Цель: Освоить команду run)
Задача 5: Обратная совместимость
Представьте, что ваш злой тимлид требует файл requirements.txt для деплоя на старый сервер. Сгенерируйте этот файл из вашего текущего проекта с помощью встроенной команды Poetry.
(Цель: Узнать про команду export)
?️♂️ Ответы для самопроверки:
mkdir habr-poetry-challenge && cd habr-poetry-challenge && poetry initpoetry add pendulumpoetry add ruff --group dev(или-Dв старых версиях)poetry run python main.pypoetry export -f requirements.txt --output requirements.txt
Анонсы новых статей, полезные материалы, а так же если в процессе у вас возникнут сложности, обсудить их или задать вопрос по этой статье можно в моём Telegram-сообществе. Смело заходите, если что-то пойдет не так, — постараемся разобраться вместе.
Уверен, у вас все получится. Вперед, к экспериментам
ic10
Да и Poetry, вроде, сейчас уже считается не слишком модным, все хвалят
uv.ra4hgn
да, в рамках продвижения rust в мир python, это актуально :)