Виртуальные окружения и зависимости: venv, pip, pyproject, uv
Проблема
Проект А требует Django 4.2, проект Б — Django 5.1. Если ставить пакеты в системный Python, они будут мешать друг другу, а обновление для одного проекта сломает другой. Кроме того, в Linux системный Python использует сама ОС — ломать его нельзя.
Виртуальное окружение — изолированная папка со своим интерпретатором (ссылкой на него) и своим site-packages.
venv
python -m venv .venv # создать окружение в папке .venv
source .venv/bin/activate # активировать (Linux/macOS)
.venv\Scripts\activate # активировать (Windows)
python -m pip install requests # пакеты ставятся в .venv
deactivate # выйти
Что внутри:
«Активация» просто ставит .venv/bin первым в PATH. Без активации можно вызывать напрямую: .venv/bin/python script.py.
import sys
print("интерпретатор:", sys.executable)
print("prefix:", sys.prefix)
print("base_prefix:", sys.base_prefix)
print("в виртуальном окружении:", sys.prefix != sys.base_prefix)
pip
pip install requests # последняя версия
pip install "django>=4.2,<5" # с ограничением версии
pip install -r requirements.txt # из файла
pip install -e . # текущий проект в режиме разработки (editable)
pip install --upgrade requests
pip uninstall requests
pip list # что установлено
pip show requests # подробности и зависимости пакета
pip freeze > requirements.txt # зафиксировать точные версии
Спецификаторы версий
| Запись | Значение |
|---|---|
==2.31.0 |
ровно эта версия |
>=2.31 |
не ниже |
>=4.2,<5 |
диапазон |
~=2.31 |
совместимая: >=2.31, ==2.* |
!=1.25.0 |
кроме этой |
==2.* |
любая 2.x |
Семантическое версионирование
MAJOR.MINOR.PATCH, например 2.31.0:
- PATCH — исправления ошибок, совместимо;
- MINOR — новые возможности, совместимо;
- MAJOR — несовместимые изменения.
Поэтому разумное ограничение для библиотеки — >=2.31,<3.
pyproject.toml
Единый файл конфигурации проекта (PEP 518, 621): метаданные, зависимости, настройки инструментов.
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.110",
"sqlalchemy>=2.0,<3",
"pydantic-settings>=2",
]
[project.optional-dependencies]
dev = ["pytest>=8", "ruff", "mypy"]
[project.scripts]
myapp = "myapp.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.ruff]
line-length = 100
[tool.pytest.ini_options]
testpaths = ["tests"]
pip install -e ".[dev]" поставит проект вместе с инструментами разработки.
Прямые зависимости и lock-файл
- В
pyproject.toml— прямые зависимости с диапазонами («нужна FastAPI не ниже 0.110»). - В lock-файле (
uv.lock,poetry.lock,requirements.txtизpip freeze/pip-compile) — точные версии всех пакетов, включая зависимости зависимостей.
Lock-файл гарантирует, что на CI, у коллеги и в продакшене установится одно и то же. Для приложений его коммитят, для библиотек обычно нет (библиотека должна работать с диапазоном версий).
Современные инструменты
| Инструмент | Для чего |
|---|---|
| uv | очень быстрая замена pip, venv, pip-tools, pipx и pyenv в одном (на Rust) |
| Poetry | управление зависимостями, lock-файл, сборка и публикация |
| pipx | установка CLI-утилит (black, httpie) в отдельные окружения |
| pyenv | несколько версий Python на одной машине |
| conda | окружения с не-Python зависимостями (data science) |
uv init myapp && cd myapp # создать проект с pyproject.toml
uv add fastapi # добавить зависимость (обновит pyproject и uv.lock)
uv add --dev pytest
uv run pytest # запустить в окружении проекта (создаст .venv сам)
uv sync # установить ровно то, что в uv.lock
uv python install 3.12 # поставить нужную версию Python
Переменные окружения и .env
Секреты (пароли БД, API-ключи) и настройки, зависящие от окружения, не хранят в коде. Их передают через переменные окружения (принцип 12-factor app):
import os
os.environ["DATABASE_URL"] = "postgresql://user:pass@localhost/shop" # обычно задаётся снаружи
db_url = os.environ["DATABASE_URL"] # KeyError, если не задана — и это хорошо
debug = os.getenv("DEBUG", "false").lower() == "true"
workers = int(os.getenv("WORKERS", "4"))
print(db_url, debug, workers)
Локально переменные кладут в файл .env (он в .gitignore!) и загружают через python-dotenv или pydantic-settings. В репозиторий коммитят .env.example с пустыми значениями.
.gitignore для Python
.venv/
__pycache__/
*.pyc
.env
.pytest_cache/
.mypy_cache/
.ruff_cache/
dist/
build/
*.egg-info/
.coverage
htmlcov/
Вопрос на собеседовании
«Как вы управляете зависимостями?» — Виртуальное окружение на проект; прямые зависимости в pyproject.toml с диапазонами версий; lock-файл с точными версиями для воспроизводимости (uv/Poetry/pip-tools); отдельная группа dev-зависимостей; регулярное обновление и проверка уязвимостей (pip-audit, Dependabot).
Итог
- Виртуальное окружение изолирует пакеты проекта:
python -m venv .venv. python -m pip install, спецификаторы версий, семантическое версионирование.pyproject.toml— конфигурация проекта; lock-файл — точные версии для воспроизводимости.- uv — быстрый современный инструмент «всё в одном».
- Секреты — в переменных окружения,
.envне коммитить.
Практика решено 0 из 5
Какие версии подходят под спецификатор?
| подходит | не подходит | |
|---|---|---|
Django>=4.2,<5.0 — версия 4.2.11 |
||
Django>=4.2,<5.0 — версия 5.0 |
||
requests~=2.31 — версия 2.32.3 |
||
requests~=2.31 — версия 3.0 |
||
pydantic==2.* — версия 2.7.1 |
||
numpy!=1.25.0 — версия 1.25.0 |
Зачем нужно виртуальное окружение?
Чем requirements.txt, полученный через pip freeze, отличается от списка зависимостей в pyproject.toml?
Версия библиотеки изменилась с 2.4.1 на 3.0.0. Что это означает по семантическому версионированию?
Что не нужно коммитить в Git?
Войдите, чтобы отмечать пройденные уроки.