Статьи

requirements.txt: полное руководство по управлению зависимостями в Python

Синтаксис Python

Каждый разработчик хотя бы раз попадал в сиутацию, когда на любое сообщение об ошибке вы получаете сакраментальный ответ коллеги: «А у меня работает!». 😒

Эта фраза — верный признак того, что в проекте царит хаос с зависимостями. Вы тащите проект на свой ПК, запускаете, и всё падает с ошибками. Коллега запускает у себя — всё работает. Разница? У него в системе, например, стоит pandas версии 1.5.3, а у вас 2.1.0, в котором что-то поменяли.

Чтобы таких проблем не было, человечество придумало простой инструмент — файлы зависимостей. В мире Python стандартом де-факто стал requirements.txt.

Что такое requirements.txt и зачем он нужен

Начнём с базы. requirements.txt — это текстовый файл, в котором построчно перечислены все внешние Python-пакеты (библиотеки), необходимые для работы вашего проекта.

Его главная миссия — обеспечить воспроизводимость окружения. Это означает, что любой разработчик (включая вас в будущем) на любой машине сможет воссоздать точное окружение, в котором проект гарантированно работает, одной простой командой.

Ключевые преимущества:

  1. Надёжность: Проект будет работать одинаково на машине коллеги, на сервере CI/CD и в production.
  2. Простота настройки: Новый человек в команде не тратит полдня на гуглёж и установку пакетов по одному, а просто выполняет одну команду.
  3. Явное управление версиями: Вы точно знаете, какая версия каждой библиотеки используется. Это спасает от багов, которые появляются после «тихих» обновлений зависимостей.
  4. Командная работа: Файл хранится в системе контроля версий (Git) вместе с кодом, являясь единым источником правды о зависимостях проекта.

Способ №1: Ручной труд и pip freeze

Это самый прямолинейный способ, с которого все начинают.

Шаг 0: Золотое правило виртуальных окружений

Прежде чем мы напишем хоть одну команду, запомните аксиому, высеченную в граните: один проект — одно виртуальное окружение.

Виртуальное окружение (создаваемое через venv или virtualenv) — это изолированная «песочница» для вашего проекта. Пакеты, которые вы устанавливаете в неё, не будут конфликтовать с пакетами из других проектов или с системными.

Создать и активировать окружение (на Linux/macOS):

# Создаем окружение в папке .venv
python3 -m venv .venv

# Активируем его
source .venv/bin/activate

На Windows активация будет выглядеть так: .venv\Scripts\activate.

[!WARNING] Никогда, слышите, НИКОГДА не генерируйте requirements.txt без активированного виртуального окружения. Иначе вы свалите в файл все пакеты, когда-либо установленные в вашей системе. Файл раздуется до сотен строк, и 95% из них будут не нужны вашему проекту.

Шаг 1: Устанавливаем пакеты

Теперь, когда мы в «песочнице», ставим всё, что нужно для проекта, например:

pip install requests pandas numpy

Шаг 2: «Замораживаем» зависимости

Когда проект готов и работает, мы выполняем команду, которая смотрит на все установленные в текущем окружении пакеты и выводит их в консоль в нужном формате:

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

Вуаля! Файл создан и содержит точные версии всех пакетов, которые вы установили.

Шаг 3: Устанавливаем из файла

Теперь ваш коллега (или вы на другой машине) клонирует репозиторий, создает и активирует своё виртуальное окружение и выполняет одну команду:

pip install -r requirements.txt

pip прочитает файл и установит все перечисленные в нём пакеты указанных версий. Проблема «а у меня работает» решена. На базовом уровне.

Проблема с pip freeze: Неявные зависимости

Казалось бы, всё идеально. Но у pip freeze есть фундаментальный недостаток, который превращает управление зависимостями в минное поле.

pip freeze не отличает пакеты, которые вы установили напрямую, от пакетов, которые были установлены как зависимости других пакетов.

Представьте, что вы установили только pandas.

pip install pandas

Но pandas сам по себе зависит от numpy, pytz, python-dateutil и других. pip любезно установит их за вас. В результате ваш pip freeze покажет кучу пакетов, хотя вы просили только один. Это так называемые транзитивные (или неявные) зависимости.

В чём проблема?

  1. Мусор в requirements.txt: Файл разрастается, и становится непонятно, какие пакеты являются ядром вашего проекта, а какие — вспомогательные.
  2. Сложность обновления: Вы хотите обновить pandas. Вы обновляете его, но что делать с его старыми зависимостями? Может, они больше не нужны? Или нужны, но других версий? pip freeze не даёт ответа.
  3. Скрытые конфликты: Через полгода вы решаете удалить pandas, потому что перешли на polars. Вы удаляете строчку pandas==... из requirements.txt. Но что насчет numpy? Он остался в файле, хотя, возможно, был нужен только для pandas. А теперь он мёртвым грузом висит в зависимостях и потенциально может конфликтовать с зависимостями polars.

Этот хаос приводит к тому, что requirements.txt, сгенерированный через pip freeze, со временем превращается в запутанный и хрупкий артефакт, который страшно трогать.

К счастью, есть способ лучше.

Способ №2: pip-tools

pip-tools — это небольшой набор утилит, который разделяет понятия: «то, что я хочу установить» и «то, что для этого реально нужно».

Основная идея: вы вручную ведёте простой файл с корневыми зависимостями (например, requirements.in), а pip-tools на его основе генерирует исчерпывающий и закомментированный requirements.txt со всеми транзитивными зависимостями.

Давайте посмотрим, как это работает.

Шаг 1: Установка pip-tools

pip-tools — это такой же пакет, но его лучше установить отдельно от проекта, например, глобально или через pipx. Но для простоты сейчас установим его в наше виртуальное окружение.

pip install pip-tools

Шаг 2: Создаём requirements.in

В корне проекта создаём файл requirements.in. В нём мы перечисляем только те пакеты, которые нам нужны напрямую. Без версий, если нам не важна конкретная, или с версиями, если важна.

# requirements.in

pandas
requests>=2.20.0

Смотрите, как чисто! Только две строки. Это наши прямые зависимости.

Шаг 3: Компиляция зависимостей

Теперь магия. Запускаем команду pip-compile:

pip-compile requirements.in

Эта команда сделает следующее:

  1. Прочитает ваш requirements.in.
  2. Найдёт самые свежие версии pandas и requests, удовлетворяющие вашим ограничениям (для requests это >=2.20.0).
  3. Рекурсивно найдёт все их зависимости (numpy, urllib3 и т.д.).
  4. Разрешит все возможные конфликты версий.
  5. Сгенерирует файл 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, и все его зависимости, которые больше никем не используются. Чистота и порядок!

Шаг 4: Установка и обновление

Установка происходит как обычно:

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.

Способ №3: Poetry и PDM

Но есть ещё более продвинутый способ — Poetry и PDM. Они не просто управляют зависимостями, они берут на себя весь жизненный цикл проекта: создание, управление окружением, зависимости, сборку и публикацию.

Эти инструменты строятся на современном стандарте pyproject.toml (PEP 518, PEP 621), который призван стать единой точкой конфигурации для любого Python-проекта.

Философия «Всё в одном»

Poetry и PDM (Python Development Master) решают ту же проблему, что и pip-tools, но подходят к ней комплексно. Вместо набора отдельных файлов (requirements.in, requirements.txt) и команд (pip, pip-compile), вы получаете единый интерфейс.

Что они делают:

  1. Инициализация проекта: Команда poetry new my-project или pdm init создаёт всю базовую структуру проекта, включая pyproject.toml.
  2. Управление зависимостями: Вы добавляете зависимости командой poetry add pandas или pdm add pandas. Инструмент сам находит нужную версию, разрешает конфликты, добавляет её в pyproject.toml и устанавливает. Никаких ручных правок файлов.
  3. Файл блокировки (Lock file): Аналогично requirements.txt, эти инструменты создают poetry.lock или pdm.lock. Это JSON-файл, который содержит точное дерево всех зависимостей с их версиями и хэшами. Он гарантирует, что команда poetry install или pdm install воссоздаст окружение с точностью до бита.
  4. Управление виртуальным окружением: Они автоматически создают и управляют виртуальным окружением для вашего проекта. Вам больше не нужно думать про venv и его активацию. Вы просто запускаете свои скрипты через poetry run python my_script.py или pdm run ..., и команда выполнится в правильном окружении.

Пример рабочего процесса с Poetry

Давайте посмотрим, как изменится наш флоу:

  1. Создаём новый проект:

    poetry new awesome-project
    cd awesome-project
    

    Poetry создаст папку с файлом pyproject.toml внутри.

  2. Добавляем зависимости:

    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. Это позволяет безопасно обновлять минорные версии, не боясь сломать код.

  3. Установка для коллеги: Коллега клонирует репозиторий и выполняет:

    poetry install
    

    Poetry читает poetry.lock и устанавливает всё точь-в-точь как у вас.

Poetry vs. PDM vs. pip-tools

Инструмент Управление зависимостями Управление окружением Сборка и публикация Основной файл
pip + venv + pip-tools Да Нет (нужен venv) Нет (нужен build/twine) requirements.in / .txt
Poetry Да Да Да pyproject.toml / poetry.lock
PDM Да Да Да pyproject.toml / pdm.lock

Когда выбирать Poetry/PDM?

  • Вы начинаете новый проект с нуля.
  • Вы создаёте библиотеку для публикации в PyPI.
  • Вам нужен единый инструмент для всего, и вы готовы потратить немного времени на его изучение.
  • Вы устали жонглировать pip, venv, pip-tools, twine и хотите простоты.

Когда остаться на pip-tools?

  • У вас уже есть большой проект с устоявшимся процессом на requirements.txt.
  • Вам нужна максимальная простота и прозрачность без дополнительной "магии".
  • Политика вашей компании или ограничения окружения не позволяют использовать сторонние менеджеры проектов.

Продвинутые техники и лучшие практики

Независимо от выбранного инструмента, есть несколько приёмов, которые помогут вам в управлении зависимостями.

1. Разделение зависимостей: Production vs. Development

В любом проекте есть зависимости, которые нужны для работы приложения в 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.

  • На production-сервере: pip install -r requirements.txt
  • Локально у разработчика: pip install -r dev-requirements.txt

Решение с Poetry/PDM: Эти инструменты поддерживают группы зависимостей из коробки.

# Добавляем зависимость только для разработки
poetry add pytest --group dev
pdm add pytest --dev

Это добавит pytest в специальную секцию в pyproject.toml, и по умолчанию poetry install / pdm install установят всё вместе. А на сервере можно будет дать команду установить только production-зависимости.

2. Стратегии версионирования: ==, >=, ~=, ^

Как именно указывать версии?

  • 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 делают это за вас автоматически.

3. Безопасность прежде всего: хэши и аудит

Как убедиться, что пакет, который вы скачиваете, не был подменён злоумышленником? Для этого существуют хэши.

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 для нового проекта.

Ваши коллеги, ваше будущее «я» и ваши пользователи скажут вам спасибо.