Каждый разработчик хотя бы раз попадал в сиутацию, когда на любое сообщение об ошибке вы получаете сакраментальный ответ коллеги: «А у меня работает!». 😒
Эта фраза — верный признак того, что в проекте царит хаос с зависимостями. Вы тащите проект на свой ПК, запускаете, и всё падает с ошибками. Коллега запускает у себя — всё работает. Разница? У него в системе, например, стоит pandas версии 1.5.3, а у вас 2.1.0, в котором что-то поменяли.
Чтобы таких проблем не было, человечество придумало простой инструмент — файлы зависимостей. В мире Python стандартом де-факто стал requirements.txt.
Начнём с базы. requirements.txt — это текстовый файл, в котором построчно перечислены все внешние Python-пакеты (библиотеки), необходимые для работы вашего проекта.
Его главная миссия — обеспечить воспроизводимость окружения. Это означает, что любой разработчик (включая вас в будущем) на любой машине сможет воссоздать точное окружение, в котором проект гарантированно работает, одной простой командой.
Ключевые преимущества:
pip freezeЭто самый прямолинейный способ, с которого все начинают.
Прежде чем мы напишем хоть одну команду, запомните аксиому, высеченную в граните: один проект — одно виртуальное окружение.
Виртуальное окружение (создаваемое через venv или virtualenv) — это изолированная «песочница» для вашего проекта. Пакеты, которые вы устанавливаете в неё, не будут конфликтовать с пакетами из других проектов или с системными.
Создать и активировать окружение (на Linux/macOS):
# Создаем окружение в папке .venv
python3 -m venv .venv
# Активируем его
source .venv/bin/activate
На Windows активация будет выглядеть так: .venv\Scripts\activate.
[!WARNING] Никогда, слышите, НИКОГДА не генерируйте
requirements.txtбез активированного виртуального окружения. Иначе вы свалите в файл все пакеты, когда-либо установленные в вашей системе. Файл раздуется до сотен строк, и 95% из них будут не нужны вашему проекту.
Теперь, когда мы в «песочнице», ставим всё, что нужно для проекта, например:
pip install requests pandas numpy
Когда проект готов и работает, мы выполняем команду, которая смотрит на все установленные в текущем окружении пакеты и выводит их в консоль в нужном формате:
pip freeze
Вывод будет примерно таким:
certifi==2023.7.22
charset-normalizer==3.3.0
idna==3.4
numpy==1.26.0
pandas==2.1.0
python-dateutil==2.8.2
pytz==2023.3.post1
requests==2.31.0
six==1.16.0
tzdata==2023.3
urllib3==2.0.6
Чтобы сохранить этот вывод в файл, используем перенаправление:
pip freeze > requirements.txt
Вуаля! Файл создан и содержит точные версии всех пакетов, которые вы установили.
Теперь ваш коллега (или вы на другой машине) клонирует репозиторий, создает и активирует своё виртуальное окружение и выполняет одну команду:
pip install -r requirements.txt
pip прочитает файл и установит все перечисленные в нём пакеты указанных версий. Проблема «а у меня работает» решена. На базовом уровне.
pip freeze: Неявные зависимостиКазалось бы, всё идеально. Но у pip freeze есть фундаментальный недостаток, который превращает управление зависимостями в минное поле.
pip freeze не отличает пакеты, которые вы установили напрямую, от пакетов, которые были установлены как зависимости других пакетов.
Представьте, что вы установили только pandas.
pip install pandas
Но pandas сам по себе зависит от numpy, pytz, python-dateutil и других. pip любезно установит их за вас. В результате ваш pip freeze покажет кучу пакетов, хотя вы просили только один. Это так называемые транзитивные (или неявные) зависимости.
В чём проблема?
requirements.txt: Файл разрастается, и становится непонятно, какие пакеты являются ядром вашего проекта, а какие — вспомогательные.pandas. Вы обновляете его, но что делать с его старыми зависимостями? Может, они больше не нужны? Или нужны, но других версий? pip freeze не даёт ответа.pandas, потому что перешли на polars. Вы удаляете строчку pandas==... из requirements.txt. Но что насчет numpy? Он остался в файле, хотя, возможно, был нужен только для pandas. А теперь он мёртвым грузом висит в зависимостях и потенциально может конфликтовать с зависимостями polars.Этот хаос приводит к тому, что requirements.txt, сгенерированный через pip freeze, со временем превращается в запутанный и хрупкий артефакт, который страшно трогать.
К счастью, есть способ лучше.
pip-toolspip-tools — это небольшой набор утилит, который разделяет понятия: «то, что я хочу установить» и «то, что для этого реально нужно».
Основная идея: вы вручную ведёте простой файл с корневыми зависимостями (например, requirements.in), а pip-tools на его основе генерирует исчерпывающий и закомментированный requirements.txt со всеми транзитивными зависимостями.
Давайте посмотрим, как это работает.
pip-toolspip-tools — это такой же пакет, но его лучше установить отдельно от проекта, например, глобально или через pipx. Но для простоты сейчас установим его в наше виртуальное окружение.
pip install pip-tools
requirements.inВ корне проекта создаём файл requirements.in. В нём мы перечисляем только те пакеты, которые нам нужны напрямую. Без версий, если нам не важна конкретная, или с версиями, если важна.
# requirements.in
pandas
requests>=2.20.0
Смотрите, как чисто! Только две строки. Это наши прямые зависимости.
Теперь магия. Запускаем команду pip-compile:
pip-compile requirements.in
Эта команда сделает следующее:
requirements.in.pandas и requests, удовлетворяющие вашим ограничениям (для requests это >=2.20.0).numpy, urllib3 и т.д.).requirements.txt со всеми пакетами и их зафиксированными версиями.А теперь взгляните на результат. Сгенерированный requirements.txt будет выглядеть примерно так:
#
# This file is autogenerated by pip-compile with Python 3.11
# To update, run:
#
# pip-compile requirements.in
#
certifi==2023.7.22
# via requests
charset-normalizer==3.3.0
# via requests
idna==3.4
# via requests
numpy==1.26.0
# via pandas
pandas==2.1.0
# via -r requirements.in
python-dateutil==2.8.2
# via pandas
pytz==2023.3.post1
# via pandas
requests==2.31.0
# via
# -r requirements.in
six==1.16.0
# via python-dateutil
tzdata==2023.3
# via pandas
urllib3==2.0.6
# via requests
Что мы видим?
==).# via pandas).Теперь, если вы захотите удалить pandas, вы просто удаляете одну строку из requirements.in, снова запускаете pip-compile, и он автоматически удалит из requirements.txt и pandas, и все его зависимости, которые больше никем не используются. Чистота и порядок!
Установка происходит как обычно:
pip install -r requirements.txt
А чтобы обновить все пакеты до последних версий, достаточно запустить:
pip-compile --upgrade requirements.in
pip-tools найдёт самые свежие версии ваших корневых пакетов и их зависимостей и перегенерирует requirements.txt.
Сравним подходы в таблице:
| Критерий | pip freeze |
pip-tools (pip-compile) |
|---|---|---|
| Источник правды | Состояние виртуального окружения | Файл requirements.in |
| Явные vs Неявные | Сваливает всё в одну кучу | Чётко разделяет и документирует |
| Обновление | Ручное и рискованное | Автоматическое и безопасное |
| Удаление | Оставляет "мусорные" зависимости | Автоматически удаляет ненужное |
| Воспроизводимость | Хорошая | Идеальная |
Вывод: pip-tools — это шаг вперёд по сравнению с pip freeze. Он вносит ясность, автоматизирует рутину и делает управление зависимостями предсказуемым и безопасным. Если вы всё ещё используете pip freeze в своих проектах, то перейдите на pip-tools.
Но есть ещё более продвинутый способ — Poetry и PDM. Они не просто управляют зависимостями, они берут на себя весь жизненный цикл проекта: создание, управление окружением, зависимости, сборку и публикацию.
Эти инструменты строятся на современном стандарте pyproject.toml (PEP 518, PEP 621), который призван стать единой точкой конфигурации для любого Python-проекта.
Poetry и PDM (Python Development Master) решают ту же проблему, что и pip-tools, но подходят к ней комплексно. Вместо набора отдельных файлов (requirements.in, requirements.txt) и команд (pip, pip-compile), вы получаете единый интерфейс.
Что они делают:
poetry new my-project или pdm init создаёт всю базовую структуру проекта, включая pyproject.toml.poetry add pandas или pdm add pandas. Инструмент сам находит нужную версию, разрешает конфликты, добавляет её в pyproject.toml и устанавливает. Никаких ручных правок файлов.requirements.txt, эти инструменты создают poetry.lock или pdm.lock. Это JSON-файл, который содержит точное дерево всех зависимостей с их версиями и хэшами. Он гарантирует, что команда poetry install или pdm install воссоздаст окружение с точностью до бита.venv и его активацию. Вы просто запускаете свои скрипты через poetry run python my_script.py или pdm run ..., и команда выполнится в правильном окружении.Давайте посмотрим, как изменится наш флоу:
Создаём новый проект:
poetry new awesome-project
cd awesome-project
Poetry создаст папку с файлом pyproject.toml внутри.
Добавляем зависимости:
poetry add pandas
poetry add requests
Poetry обновит pyproject.toml, решит все зависимости и создаст poetry.lock.
Раздел зависимостей в pyproject.toml будет выглядеть так:
[tool.poetry.dependencies]
python = "^3.11"
pandas = "^2.1.0"
requests = "^2.31.0"
Символ ^ (карет) — это умное ограничение версии, означающее "совместимо с". ^2.1.0 значит >=2.1.0, <3.0.0. Это позволяет безопасно обновлять минорные версии, не боясь сломать код.
Установка для коллеги: Коллега клонирует репозиторий и выполняет:
poetry install
Poetry читает poetry.lock и устанавливает всё точь-в-точь как у вас.
| Инструмент | Управление зависимостями | Управление окружением | Сборка и публикация | Основной файл |
|---|---|---|---|---|
pip + venv + pip-tools |
Да | Нет (нужен venv) |
Нет (нужен build/twine) |
requirements.in / .txt |
Poetry |
Да | Да | Да | pyproject.toml / poetry.lock |
PDM |
Да | Да | Да | pyproject.toml / pdm.lock |
Когда выбирать Poetry/PDM?
pip, venv, pip-tools, twine и хотите простоты.Когда остаться на pip-tools?
requirements.txt.Независимо от выбранного инструмента, есть несколько приёмов, которые помогут вам в управлении зависимостями.
В любом проекте есть зависимости, которые нужны для работы приложения в production (например, pandas, requests), а есть те, что нужны только для разработки: тестирование, линтинг, форматирование (pytest, black, ruff, mypy).
Сваливать их в один requirements.txt — плохая практика. Это раздувает production-окружение и увеличивает поверхность для потенциальных атак.
Решение с pip-tools:
Создайте несколько .in файлов.
requirements.in — только для production.dev-requirements.in — для разработки.# dev-requirements.in
# Подключаем прод-зависимости
-c requirements.txt
# Инструменты для разработки
pytest
black
ruff
Теперь компилируем оба файла:
pip-compile requirements.in
pip-compile dev-requirements.in
Вы получите requirements.txt и dev-requirements.txt.
pip install -r requirements.txtpip install -r dev-requirements.txtРешение с Poetry/PDM:
Эти инструменты поддерживают группы зависимостей из коробки.
# Добавляем зависимость только для разработки
poetry add pytest --group dev
pdm add pytest --dev
Это добавит pytest в специальную секцию в pyproject.toml, и по умолчанию poetry install / pdm install установят всё вместе. А на сервере можно будет дать команду установить только production-зависимости.
==, >=, ~=, ^Как именно указывать версии?
requests==2.31.0: Жёсткая фиксация. Максимально надёжно, но требует ручного обновления. Идеально для requirements.txt / lock-файлов.requests>=2.31.0: Минимальная версия. Разрешает любые обновления, включая мажорные. Опасно, может сломать код. Используйте с осторожностью.requests~=2.31.0: Совместимый релиз. Означает >=2.31.0, <2.32.0. Разрешает только патч-обновления. Хороший выбор для библиотек.requests^2.31.0: Совместимый мажорный релиз (в стиле Poetry/NPM). Означает >=2.31.0, <3.0.0. Разрешает патчи и минорные обновления. Идеально для приложений, так как предполагает, что минорные версии не ломают обратную совместимость.Рекомендация: в requirements.in (или pyproject.toml) используйте гибкие ограничения (~= или ^), а в сгенерированных requirements.txt (или lock-файлах) всегда должна быть жёсткая фиксация (==). pip-tools, Poetry и PDM делают это за вас автоматически.
Как убедиться, что пакет, который вы скачиваете, не был подменён злоумышленником? Для этого существуют хэши.
pip-tools может добавлять ожидаемые хэши для каждого пакета в requirements.txt:
pip-compile --generate-hashes requirements.in
Файл будет выглядеть так:
pandas==2.1.0 \
--hash=sha256:.... \
--hash=sha256:....
При установке (pip install -r requirements.txt) pip будет не только скачивать пакет, но и проверять, совпадает ли его хэш с указанным в файле. Если нет — установка прервётся. Это пуленепробиваемая защита от атаки на цепочку поставок.
Poetry и PDM делают это по умолчанию в своих lock-файлах.
Кроме того, с версии 23.0 в pip встроена утилита для аудита уязвимостей:
pip audit
Эта команда проверит все установленные пакеты и их версии на наличие известных уязвимостей в базе данных PyPI. Регулярно запускайте её в своих CI/CD пайплайнах!
Ваша поддержка — это энергия для новых статей и проектов. Спасибо, что читаете!
| Ваш уровень / задача | Рекомендуемый инструмент | Почему? |
|---|---|---|
| Начинающий / Небольшой скрипт | pip freeze в venv |
Просто для понимания основ, но быстро переходите дальше. |
| Серьёзный проект / Командная работа | pip-tools |
Золотой стандарт. Просто, мощно, прозрачно. Легко внедрить в существующий проект. |
| Новый проект / Библиотека / "Хочу всё и сразу" | Poetry или PDM |
Единый интерфейс для всего жизненного цикла проекта. Современный подход. |
Уже использую Poetry |
Продолжайте использовать Poetry |
Отличный, зрелый инструмент. Нет причин переходить. |
Хочу фишки Poetry, но без "магии" с venv |
PDM |
PDM более гибкий в управлении окружениями и следует стандартам PEP ближе. |
Управление зависимостями — это фундамент стабильности и безопасности вашего проекта. Хаос в requirements.txt сегодня — это баги, конфликты и головная боль завтра.
Начните с малого: заведите привычку всегда использовать виртуальные окружения. Затем сделайте следующий шаг: замените pip freeze на pip-tools. А когда будете готовы, попробуйте Poetry или PDM для нового проекта.
Ваши коллеги, ваше будущее «я» и ваши пользователи скажут вам спасибо.