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)
Семантика версий
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
Проблемы:
-
Смешаны прямые и транзитивные. Ты сам ставил только
fastapiиrequests. Остальное притянулось. Из файла не видно что важно, а что вспомогательное. -
Нет lock-файла отдельно от спецификации. Если завтра выйдет
requests 2.32и ты захочешь обновиться - не понятно как: переписать вручную? Запуститьpip install -U? -
Нет групп зависимостей. В реальном проекте есть зависимости только для разработки (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 ".[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 |
| Библиотека для PyPI | poetry или hatch + pyproject.toml |
| Data science | conda (отдельная экосистема) |
| Монорепо с несколькими сервисами | 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
Несколько правил безопасности:
-
Никогда не запускай
pip installбез venv в системном Python - особенно сsudo. Можно сломать системные инструменты ОС. -
Не доверяй незнакомым пакетам. На PyPI бывают пакеты-typosquatting -
requetsвместоrequests. Проверяй имя. -
Используй
--require-hashesдля критичных проектов. Hash-фиксация проверяет что скачиваемый пакет совпадает с известным хешем. -
Обновляй pip регулярно:
python3 -m pip install --upgrade pip
Мини-задание
- В новом проекте создай
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",
]
- Создай venv и установи проект в editable-режиме:
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
- Проверь что установлены и
requests, иpytest:
pip list
- Создай файл
test_demo.py:
import requests
def test_zen():
response = requests.get("https://api.github.com/zen")
assert response.status_code == 200
- Запусти тесты:
pytest test_demo.py -v. Убедись что тест прошёл.
Что дальше
Освоили базовые инструменты управления зависимостями. В уроке про Docker и CI увидишь, как этот же pyproject.toml попадает в образ и в пайплайн. В следующем уроке - REPL и отладка: как эффективно использовать python3 -i, breakpoint(), pdb и ipython чтобы быстро понимать что происходит в коде во время разработки.