pip, requirements.txt и pyproject.toml

В прошлом уроке про venv мы поставили requests через pip install и зафиксировали зависимости через pip freeze. Этого хватает для разовых скриптов, но в серьёзных проектах схема не масштабируется: непонятно где «прямые» зависимости (то что ты сам выбрал), а где «транзитивные» (то что притянулось как зависимости зависимостей). В этом уроке - как организовать зависимости профессионально.

pip как менеджер пакетов

pip - стандартный установщик пакетов из репозитория PyPI (Python Package Index). Базовые команды:

pip install requests              # последняя стабильная
pip install requests==2.31.0      # точная версия
pip install "requests>=2.30,<3"   # диапазон версий
pip install -U requests           # обновить
pip uninstall requests            # удалить
pip list                          # список установленных
pip show requests                 # подробности о пакете
pip search ...                    # поиск (отключён в 2020, используют сайт PyPI)
PyPI (Python Package Index) - центральный репозиторий пакетов Python на pypi.org. Когда ты пишешь `pip install requests`, он скачивает пакет именно оттуда. Существуют и приватные индексы для корпоративных пакетов - подключаются через `--index-url`.

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

pip понимает несколько форматов спецификации версий:

СпецификацияЧто значит
requestsЛюбая версия (обычно последняя стабильная)
requests==2.31.0Ровно эта версия
requests>=2.30Не ниже 2.30
requests~=2.31Совместимая (≥2.31, <3.0)
requests~=2.31.0Совместимая патч-версия (≥2.31.0, <2.32)
requests>=2.30,<3Диапазон
requests!=2.30.1Любая кроме 2.30.1 (если известный баг)

Для backend-проектов рекомендуют пиннинг точных версий в lock-файле - чтобы локально, на CI и в продакшене стояли идентичные пакеты. Это исключает «у меня работает, на сервере падает».

Проблема plain requirements.txt

pip freeze > requirements.txt создаёт файл с примерно таким содержимым:

certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
fastapi==0.110.0
h11==0.14.0
idna==3.6
pydantic==2.6.1
pydantic_core==2.16.2
requests==2.31.0
sniffio==1.3.0
starlette==0.36.3
typing_extensions==4.10.0
urllib3==2.2.0

Проблемы:

  1. Смешаны прямые и транзитивные. Ты сам ставил только fastapi и requests. Остальное притянулось. Из файла не видно что важно, а что вспомогательное.

  2. Нет lock-файла отдельно от спецификации. Если завтра выйдет requests 2.32 и ты захочешь обновиться - не понятно как: переписать вручную? Запустить pip install -U?

  3. Нет групп зависимостей. В реальном проекте есть зависимости только для разработки (pytest, black, mypy) и для продакшена (fastapi, sqlalchemy). В requirements.txt они слиты в один список.

Решения исторически было два: разделить на несколько файлов (requirements.txt, requirements-dev.txt) или использовать pip-tools для генерации lock из спецификации. В 2024+ оба подхода вытесняет pyproject.toml.

pyproject.toml - современный стандарт

pyproject.toml - декларативный конфиг проекта, описанный в PEP 518/621. Не путать с requirements.txt: это спецификация проекта целиком (имя, версия, зависимости, билд-система), а не просто список пакетов.

Минимальный пример:

[project]
name = "my-backend"
version = "0.1.0"
description = "FastAPI backend service"
requires-python = ">=3.11"

dependencies = [
    "fastapi>=0.110,<1.0",
    "pydantic>=2.6,<3.0",
    "uvicorn[standard]>=0.27",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-asyncio>=0.23",
    "black>=24.0",
    "mypy>=1.8",
]

Здесь:

  • [project] - метаданные пакета
  • dependencies - прямые зависимости для работы приложения
  • [project.optional-dependencies] - группы дополнительных зависимостей (dev, test, docs). Сюда обычно попадают pytest, ruff и mypy
`pip install -e .` ставит текущий проект как editable пакет, читая зависимости из `pyproject.toml`.

pip install -e ".[dev]" ставит проект плюс группу dev из optional-dependencies.

poetry - менеджер на основе pyproject.toml

Poetry - популярный инструмент, который активно использует pyproject.toml:

# установить poetry
pip install poetry

# создать новый проект
poetry new my-backend
cd my-backend

# добавить зависимость
poetry add fastapi
poetry add pytest --group dev

# установить всё
poetry install

# запустить команду в окружении проекта
poetry run python main.py
poetry run pytest

Poetry автоматически:

  • Создаёт виртуальное окружение
  • Поддерживает pyproject.toml и poetry.lock (файл с точными версиями всех зависимостей включая транзитивные)
  • Решает конфликты зависимостей до установки
  • Умеет публиковать пакеты в PyPI

poetry.lock обязательно коммитят в git - это гарантия что у всех в команде и на CI стоит ровно одно и то же.

uv - быстрая альтернатива

uv от Astral (создатели ruff) - drop-in замена pip и venv, в 10-100 раз быстрее:

# установка
curl -LsSf https://astral.sh/uv/install.sh | sh

# создать venv
uv venv

# установить пакет
uv pip install fastapi

# установить из pyproject.toml
uv pip install -e .

uv поддерживает все стандартные форматы (requirements.txt, pyproject.toml) и активно догоняет poetry по фичам. Для новых проектов в 2026 году многие команды выбирают именно его за скорость.

Какой инструмент выбирать

СценарийРекомендация
Учебный скрипт, один файлvenv + pip + requirements.txt
Backend-приложениеpoetry или uv + pyproject.toml
Библиотека для PyPIpoetry или hatch + pyproject.toml
Data scienceconda (отдельная экосистема)
Монорепо с несколькими сервисамиuv (быстрее всех на CI)

В этом курсе мы для простоты используем pip и venv - чтобы не отвлекаться на инструменты. В реальном проекте сразу берите poetry или uv.

Lock-файлы и воспроизводимость

Lock-файл - файл с точными версиями всех зависимостей, включая транзитивные. Примеры:

  • poetry.lock (poetry)
  • Pipfile.lock (pipenv)
  • requirements.txt после pip freeze (упрощённый lock)
  • uv.lock (uv, после uv lock)

Зачем lock-файлы:

  • Воспроизводимость: на CI и в продакшене стоит то же самое что у разработчика
  • Безопасность: не подтянется заражённая версия пакета, если кто-то её опубликует
  • Скорость: установщик не тратит время на разрешение версий, всё уже зафиксировано

Без lock-файла pip install может на разных машинах подтянуть разные версии, и проблемы будут плавать.

Опасности pip

Несколько правил безопасности:

  1. Никогда не запускай pip install без venv в системном Python - особенно с sudo. Можно сломать системные инструменты ОС.

  2. Не доверяй незнакомым пакетам. На PyPI бывают пакеты-typosquatting - requets вместо requests. Проверяй имя.

  3. Используй --require-hashes для критичных проектов. Hash-фиксация проверяет что скачиваемый пакет совпадает с известным хешем.

  4. Обновляй pip регулярно:

python3 -m pip install --upgrade pip

Мини-задание

  1. В новом проекте создай pyproject.toml:
[project]
name = "my-test"
version = "0.1.0"
requires-python = ">=3.11"

dependencies = [
    "requests>=2.30",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
]
  1. Создай venv и установи проект в editable-режиме:
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
  1. Проверь что установлены и requests, и pytest:
pip list
  1. Создай файл test_demo.py:
import requests

def test_zen():
    response = requests.get("https://api.github.com/zen")
    assert response.status_code == 200
  1. Запусти тесты: pytest test_demo.py -v. Убедись что тест прошёл.

Что дальше

Освоили базовые инструменты управления зависимостями. В уроке про Docker и CI увидишь, как этот же pyproject.toml попадает в образ и в пайплайн. В следующем уроке - REPL и отладка: как эффективно использовать python3 -i, breakpoint(), pdb и ipython чтобы быстро понимать что происходит в коде во время разработки.

Зарегистрируйтесь бесплатно, чтобы пройти квиз, решить задание с автопроверкой и вести прогресс.