Как создать Python-проект с uv и сохранить зависимости: выполните пять шагов. uv init формирует каркас, .python-version фиксирует интерпретатор, uv add записывает зависимость в pyproject.toml и uv.lock, uv run исполняет код, а uv sync --locked воспроизводит окружение у коллеги. Ниже — порядок команд, назначение каждого файла и проверка результата.
Создание структуры проекта через uv init
Документация uv описывает проект как набор частей, которые работают вместе и позволяют инструменту управлять зависимостями. Стартовая команда — uv init. Документация проекта приводит два варианта: создать каталог сразу с именем проекта (uv init hello-world, затем cd hello-world) либо инициализировать проект в текущем рабочем каталоге (mkdir hello-world, cd hello-world, uv init).
После выполнения команды документация uv перечисляет появившиеся объекты: каталог .git/, файл .gitignore, файл .python-version, файл pyproject.toml, файл README.md и каталог src с пакетом hello_world, внутри которого лежит __init__.py. Файл pyproject.toml описывает точку входа hello-world, ссылающуюся на простую программу в __init__.py.
Проверить каркас можно командой uv run hello-world: документация uv показывает ожидаемый вывод Hello from hello-world!. Этот запуск подтверждает, что структура собрана верно и точка входа работает.
Для учебного примера из этой статьи удобнее явно создать свой файл main.py в корне проекта. Документация uv не утверждает, что uv init всегда создаёт main.py по умолчанию, поэтому файл добавляем руками. Такой подход даёт предсказуемую точку запуска и не зависит от шаблона пакета.
Порядок действий для старта:
uv init --python 3.12— создаёт каркас и фиксирует версию интерпретатора.- Создать файл
main.pyв корне проекта с учебным кодом. uv run main.py— запустить код в окружении проекта.
Каталог .git/ и .gitignore появляются сразу, поэтому проект готов к версионированию. Файл .python-version и pyproject.toml попадают под контроль версий вместе с исходниками.
Выбор интерпретатора и файл .python-version
Документация uv определяет версию Python как комбинацию интерпретатора (исполняемого файла python), стандартной библиотеки и вспомогательных файлов. Поскольку на машине часто уже стоит системный Python, uv умеет находить установленные версии. Документация uv разделяет два типа установок: managed Python installations — версии, которые uv ставит сам, и system Python installations — все остальные.
Документация uv отдельно отмечает: инструмент не различает версии, установленные операционной системой, и версии, установленные и управляемые другими инструментами. Для практики это значит, что при выборе интерпретатора полезно явно указать нужную версию.
Файл .python-version хранит выбранную версию и делает выбор воспроизводимым. При инициализации версия задаётся флагом --python, как в команде uv init --python 3.12. Дальше uv использует это значение при создании окружения и при запуске команд.
Проверить фактическую версию интерпретатора можно из самого кода. Учебный main.py печатает sys.version_info.major, и для Python 3 ожидаемая строка — 3. Это даёт читателю простую проверку: если вывод отличается, интерпретатор выбран другой.
Таблица ниже сопоставляет артефакты выбора версии и их роль.
| Артефакт | Где живёт | Назначение |
|---|---|---|
.python-version |
корень проекта, в Git | Фиксирует выбранную версию интерпретатора |
pyproject.toml |
корень проекта, в Git | Описывает проект и его зависимости |
uv.lock |
корень проекта, в Git | Фиксирует разрешённые версии пакетов |
.venv |
корень проекта, локально | Содержит установленные пакеты окружения |
pyproject.toml, uv.lock и .venv: назначение артефактов
Документация uv описывает проект как набор частей, работающих вместе. Ключевая часть — pyproject.toml: документация uv указывает, что проекты определяют свои зависимости именно в этом файле. Это декларация намерений: какие пакеты нужны и в каких диапазонах версий.
Второй артефакт — uv.lock. Документация uv определяет locking как процесс разрешения зависимостей проекта в lock-файл. Проще говоря, uv.lock хранит конкретные версии, выбранные из диапазонов pyproject.toml. Этот файл фиксируется в Git, чтобы у всех участников совпадали версии пакетов.
Третий артефакт — .venv. Документация uv определяет syncing как установку подмножества пакетов из lock-файла в окружение проекта. Каталог .venv — это локальное окружение, которое собирается на конкретной машине. В Git его не кладут: он воспроизводится из uv.lock командой синхронизации.
Документация uv подчёркивает, что locking и syncing в uv автоматические. Например, при вызове uv run проект сначала блокируется и синхронизируется, и только потом запускается запрошенная команда. Это снимает необходимость вручную вызывать отдельные шаги при обычной работе.
Разделение ролей помогает понять, что коммитить. pyproject.toml, uv.lock и .python-version идут в репозиторий. .venv остаётся локальным. Секреты и локальные переменные окружения в репозиторий не попадают.
Добавление зависимости и запуск через uv run
Зависимость добавляется командой uv add. В учебном примере показывается uv add requests — отдельная команда, которая записывает пакет в проект. Сетевой вызов из самого примера не выполняется: команда приведена как шаг, который читатель делает у себя.
Что происходит при uv add: пакет попадает в pyproject.toml как зависимость, а uv.lock обновляется с разрешённой версией. Документация uv описывает locking как разрешение зависимостей в lock-файл, и добавление пакета запускает этот процесс.
Запуск кода идёт через uv run main.py. Документация uv прямо указывает, что при использовании uv run проект блокируется и синхронизируется перед вызовом команды. Это значит, что перед исполнением uv приводит окружение в соответствие с uv.lock.
Учебный main.py для проверки:
import sys
print(sum([2, 3, 5]))
print(sys.version_info.major)
Ожидаемый вывод — две строки: сначала 10, затем 3. Первая строка — результат сложения чисел в списке, вторая — мажорная версия интерпретатора. Значения проверены как логика Python; uv при подготовке статьи не запускался.
Пример проверяет выполнение кода и мажорную версию интерпретатора. Он не различает минорные версии Python. Если первая строка отличается, проблема в коде. Если вторая строка не 3, стоит перепроверить .python-version.
Передача проекта коллеге и uv sync —locked
Для передачи проекта коллеге нужен минимальный набор: исходники, pyproject.toml, uv.lock и .python-version. Каталог .venv не передаётся — он воспроизводится на новой машине.
Коллега выполняет uv sync --locked. Документация uv определяет syncing как установку подмножества пакетов из lock-файла в окружение проекта. Флаг --locked требует, чтобы lock-файл соответствовал pyproject.toml, и не даёт молча обновить версии.
После синхронизации запуск идёт командой uv run --locked main.py. Флаг --locked сохраняет то же требование: исполнение идёт по зафиксированному lock-файлу. Ожидаемый вывод остаётся прежним — 10 и 3.
Таблица ниже показывает, что передаётся и что воспроизводится.
| Объект | Передаётся коллеге | Воспроизводится на месте |
|---|---|---|
| Исходники | Да | — |
pyproject.toml |
Да | — |
uv.lock |
Да | — |
.python-version |
Да | — |
.venv |
Нет | Да, через uv sync --locked |
Смысл разделения простой: декларация и фиксация версий едут вместе с кодом, а тяжёлое окружение собирается локально. Это уменьшает объём передачи и снижает риск, что чужая сборка пакетов попадёт в проект.
Полный main.py и ожидаемые строки 10 и 3
Полный учебный файл main.py состоит из трёх строк:
import sys
print(sum([2, 3, 5]))
print(sys.version_info.major)
Разберём каждую строку. import sys подключает модуль для доступа к сведениям об интерпретаторе. print(sum([2, 3, 5])) складывает числа 2, 3 и 5 и печатает результат 10. print(sys.version_info.major) печатает мажорную версию интерпретатора, для Python 3 это 3.
Порядок вывода важен: сначала 10, затем 3. Если строки поменялись местами, значит, запускается другой файл или изменён порядок инструкций. Если первая строка выводит не 10, проверьте список чисел. Если вторая строка выводит не 3, проверьте выбранный интерпретатор и файл .python-version.
Проверка результата даёт читателю опорную точку. Любое расхождение указывает на конкретный шаг: код, интерпретатор или окружение. Это дешевле, чем разбираться с ошибками установки пакетов вслепую.
Рассогласование pyproject и lock: обновление и исполнение
Отдельного внимания требует ситуация, когда pyproject.toml и uv.lock расходятся. Учебный контрольный пример: в pyproject.toml вручную изменено ограничение версии зависимости так, что зафиксированная в lock версия больше не входит в новый диапазон, а lock не обновлён. В этом случае locked-команда должна остановиться с сообщением о необходимости обновления.
Важное уточнение: само расширение диапазона зависимости не обязательно делает lock устаревшим, если зафиксированная версия по-прежнему подходит. Проверка обязана исключать выбранную версию — только тогда рассогласование реально. Это различие помогает не обновлять lock без причины.
Различие между обновлением lock и исполнением по lock принципиально. Обновление меняет зафиксированные версии и требует явного действия. Исполнение по lock использует уже зафиксированные версии и не должно их менять. Флаг --locked закрепляет второе поведение.
Практический порядок при рассогласовании:
- Проверить, входит ли зафиксированная версия в новый диапазон
pyproject.toml. - Если входит — lock остаётся актуальным, запуск по lock проходит.
- Если не входит — обновить lock явной командой, затем повторить
uv sync --locked.
Такой порядок сохраняет воспроизводимость: окружение коллеги собирается по тем же версиям, что и у автора проекта.
Частые вопросы
Как создать структуру проекта uv init?
Команда uv init создаёт каркас проекта. Документация uv перечисляет появившиеся объекты: .git/, .gitignore, .python-version, pyproject.toml, README.md и каталог src с пакетом. Инициализировать можно как новый каталог, так и текущий рабочий каталог.
Как выбрать интерпретатор и что хранит .python-version?
Версия задаётся флагом --python при инициализации, например uv init --python 3.12. Файл .python-version фиксирует выбранную версию и делает выбор воспроизводимым. Документация uv разделяет managed и system Python installations, поэтому явное указание версии надёжнее.
Чем отличаются pyproject.toml, uv.lock и .venv?
pyproject.toml описывает зависимости проекта. uv.lock фиксирует разрешённые версии пакетов. .venv — локальное окружение с установленными пакетами. Первые два файла идут в Git, .venv воспроизводится на машине.
Как добавить зависимость и запустить код через uv run?
Зависимость добавляется командой uv add, например uv add requests. Запуск идёт через uv run main.py. Документация uv указывает, что при uv run проект блокируется и синхронизируется перед вызовом команды.
Как передать проект коллеге и что делает uv sync —locked?
Коллеге передаются исходники, pyproject.toml, uv.lock и .python-version. Затем он выполняет uv sync --locked и запускает uv run --locked main.py. Флаг --locked требует соответствия lock-файла и pyproject.toml и не даёт молча обновить версии.
