uv на macOS: управление версиями Python, проектами и инструментами
Автоматический перевод
Эта статья была автоматически переведена с оригинальной английской версии.
Это руководство для Python-разработчиков на macOS, которые переходят на uv с pip, venv, pip-tools, pyenv или pipx. uv поддерживает четыре вида работы: проект, скрипт из одного файла, разовый запуск CLI через uvx и CLI, установленный в PATH. Все четыре могут использовать версии Python, которые устанавливает uv. Вы узнаете команды для каждого варианта и научитесь выбирать между ними.
Я перевёл свой Python-воркфлоу на uv. Теперь один исполняемый файл покрывает работу, которую я раньше делил между этими инструментами.
Быстрый старт
# Install uv with Homebrew
brew install uv
# Start a project
uv init --no-package example-app
cd example-app
uv add httpx
uv run python main.py
# Prints "Hello from example-app!"
# Run an isolated CLI without installing it permanently
uvx ruff check .
На схеме показано, где каждый из четырёх воркфлоу хранит своё окружение, что вы храните для каждого из них и общие для них управляемые установки Python.
Установка и обновление uv
Homebrew — удобный способ установки на macOS. Все поддерживаемые способы описаны в руководстве по установке uv:
brew install uv
uv --version
Если uv установлен через Homebrew, обновлять его тоже должен Homebrew:
brew upgrade uv
uv self update предназначен для автономного способа установки uv и отключён при установке через пакетный менеджер. Обновляйте uv тем же инструментом, которым его установили.
Чтобы узнать, где находятся uv, его установки Python, инструменты и кэш:
command -v uv
uv python dir
uv tool dir
uv cache dir
Управляемые установки Python, постоянные инструменты и одноразовые записи кэша находятся в отдельных каталогах. Кэш можно удалить и создать заново. Он не служит источником достоверных данных. На macOS uv клонирует пакеты из кэша в каждое окружение (copy-on-write), а не копирует их. При пересоздании .venv uv повторно использует нужные артефакты, которые ещё есть в кэше.
Проекты: декларации, резолвинг и окружение
Проект uv обычно состоит из трёх частей:
pyproject.tomlобъявляет метаданные проекта и прямые зависимости.uv.lockхранит кросс-платформенный результат резолвинга uv..venv— локальное установленное окружение, которое должно быть одноразовым.
На схеме файлы проекта разделены по тому, кто их пишет и хранит ли их git.
Создайте приложение и добавьте зависимости для рантайма и тестов:
uv init --no-package forecast-app
cd forecast-app
uv add httpx
uv add --group test pytest
uv run python main.py
uv run --group test pytest --version
Приложение выводит Hello from forecast-app!; последняя команда проверяет, что pytest установлен. Когда появятся тесты, запускайте их через uv run --group test pytest.
Шаблон --no-package создаёт main.py, pyproject.toml, README.md и .python-version без системы сборки. Начиная с uv 0.12, обычный uv init создаёт приложение в виде пакета со структурой src и бэкендом сборки. Используйте uv init --lib для библиотеки в виде пакета. См. руководство uv по созданию проектов.
uv run проверяет проект, при необходимости обновляет lock-файл, синхронизирует нужные зависимости и запускает команду. Этот жизненный цикл описан в документации uv по структуре проекта и синхронизации. Первая операция с проектом при необходимости создаёт .venv и uv.lock. Сам uv init создаёт только файлы проекта.
По умолчанию uv устанавливает группу dev. Другим группам, например test, нужен --group в каждой команде или default-groups в [tool.uv]. Чтобы установить только одну группу, например в CI-джобе для документации, используйте uv sync --only-group docs.
Группы остаются локальными и никогда не публикуются. Публикуемый extra — другое дело: uv add --optional cli rich добавляет extra, который пользователи устанавливают как forecast-app[cli].
Чтобы использовать проект в Jupyter-ноутбуках в VS Code, выполните uv add --dev ipykernel и выберите .venv в качестве ядра. См. руководство uv по Jupyter.
Коммитьте входные данные, а не окружение
Коммитьте pyproject.toml, uv.lock, исходный код и осознанно выбранный .python-version. Добавьте .venv и кэши uv в игнорируемые.
Коллега, который клонирует репозиторий, получает зафиксированное окружение одной командой:
git clone https://github.com/example/forecast-app.git
cd forecast-app
uv sync --locked
Чтобы узнать, почему установлен пакет, и обновить lock-файл до более новых версий:
uv tree # show the dependency graph
uv tree --outdated # show newer versions where they exist
uv lock --upgrade-package httpx # move one package, keep the rest
uv lock --upgrade # re-resolve everything
Коммитьте изменённый uv.lock вместе с изменением, которое его потребовало.
Lock-файл фиксирует резолвинг для всех поддерживаемых маркеров и платформ. Он не делает нативные wheel-пакеты одинаковыми на macOS, Linux, Intel и Apple Silicon. Тестируйте на каждой платформе деплоя.
Часть этого uv умеет проверять сам. required-environments в [tool.uv] заставляет резолвинг завершиться ошибкой, если у пакета без дистрибутива исходного кода нет wheel для одной из перечисленных платформ:
[tool.uv]
required-environments = [
"sys_platform == 'darwin' and platform_machine == 'arm64'",
"sys_platform == 'linux' and platform_machine == 'x86_64'",
]
no-build-package запрещает сборку из исходников для указанных пакетов, поэтому при отсутствии wheel установка завершается ошибкой, а не запускает компилятор. Если вы также деплоите PyTorch на Linux с CUDA, настройте индексы по руководству uv по PyTorch.
Актуальность lock-файла: --locked — это не --frozen
По умолчанию команды проекта могут обновлять uv.lock, когда меняются декларации. Флаги актуальности и точности описаны в документации uv по блокировке и синхронизации.
Используйте --locked, чтобы lock-файл обязательно соответствовал метаданным проекта:
uv lock --check
uv sync --locked --group test
uv run --locked --group test pytest
Если pyproject.toml и uv.lock расходятся, эти команды завершаются ошибкой, а не резолвят новый lock-файл. Используйте их в CI, чтобы при устаревшем lock-файле сборка завершалась ошибкой.
Используйте --frozen, только когда вы намеренно хотите, чтобы uv взял существующий lock-файл без проверки его актуальности:
uv sync --frozen
Это полезно на контролируемом этапе сборки, где lock-файл уже проверен, но устаревший lock-файл этот флаг не обнаруживает.
На схеме один устаревший проект проходит четыре команды: rich добавили в pyproject.toml вручную, а uv.lock не обновили.
Точность — отдельная настройка, не связанная с актуальностью. uv sync по умолчанию работает в точном режиме и удаляет лишние пакеты. uv run по умолчанию выполняет неточную синхронизацию, если не указан --exact. На схеме показано, что происходит с пакетом, установленным вручную, при uv sync, uv run и uv run --exact.
Установка и закрепление версий Python
uv умеет скачивать дистрибутивы Python и управлять ими:
uv python install 3.12 3.13
uv python list --only-installed
uv python pin 3.12
Управляемые сборки CPython в uv берутся из проекта python-build-standalone. uv также находит системные интерпретаторы, интерпретаторы Homebrew, pyenv, Conda и другие. Поддерживаемые источники и правила поиска описаны в документации uv по версиям Python.
.python-version — это запрос версии, который находят uv и другие совместимые инструменты. uv python pin записывает этот файл, а uv init создаёт его. requires-python в pyproject.toml — это контракт совместимости проекта. Задавайте оба значения осознанно:
[project]
requires-python = ">=3.12,<3.14"
uv берёт запрошенную версию сначала из --python, затем из .python-version, затем из requires-python.
Закрепление 3.12 не гарантирует ни неизменную патч-сборку, ни одинаковый артефакт на каждой операционной системе. Если нужна точная патч-версия, запросите её. В CI выбирайте интерпретатор и выводите его версию: через uv run --python 3.12.7 python -VV или задав UV_PYTHON=3.12.7 для джобы.
Используйте другой менеджер Python, когда проекту нужен дистрибутив или конфигурация сборки, которых uv не предоставляет. uv всё равно может использовать этот интерпретатор через --python или обычный поиск.
Выбор между uv run, uvx и установленными инструментами
Выбирайте по тому, нужны ли инструменту установленные пакеты проекта, должен ли он оставаться в PATH или может работать независимо. На схеме показаны доступные зависимости и срок существования каждого окружения.
Инструменты, связанные с проектом: uv run
Если pytest, mypy, генератору кода или другому инструменту нужно импортировать проект или использовать его зафиксированные плагины, объявите инструмент в группе зависимостей:
uv add --group lint ruff
uv run --group lint ruff check .
uvx не устанавливает пакет и зависимости проекта автоматически. Инструмент всё ещё может импортировать локальные исходные файлы, если его механизм импорта это позволяет; используйте uv run, когда ему нужен установленный проект или его зависимости.
Разовые инструменты: uvx
uvx — псевдоним для uv tool run. Он повторно использует совместимый инструмент, установленный через uv tool install; иначе он использует изолированное окружение в одноразовом кэше uv. Этот кэш и жизненный цикл постоянных инструментов описаны в документации uv по инструментам:
uvx ruff@0.12.0 check .
uvx ruff@latest check .
uvx --from jupyterlab jupyter lab
uvx --from httpie http --version
uvx --with mkdocs-material mkdocs --help
Закрепляйте версию инструмента в CI или документации. Если установленного инструмента нет, первый вызов без версии выбирает последний доступный релиз; последующие вызовы могут использовать состояние кэша повторно. @latest игнорирует установленную версию и обновляет кэш для последнего релиза. Используйте --from, когда имя пакета отличается от имени команды, как у jupyterlab и httpie.
Постоянные исполняемые файлы: uv tool install
Устанавливайте инструмент, когда скриптам, которые вы не контролируете, нужна его команда в PATH, или когда им должен управлять манифест настройки вашей машины:
uv tool install ruff
uv tool list
uv tool upgrade ruff
uv tool upgrade --all
uv tool uninstall ruff
Если установка предупреждает, что каталог исполняемых файлов отсутствует в PATH, запустите uv tool update-shell и перезапустите оболочку перед использованием команды. См. документацию uv по исполняемым файлам инструментов.
Постоянные инструменты тоже используют изолированные окружения. Не меняйте эти окружения вручную через pip.
Скрипты из одного файла с PEP 723
PEP 723 определяет метаданные внутри скрипта. Совместимый раннер читает блок комментариев и собирает изолированное окружение. Формат метаданных и поведение раннера описаны в руководстве uv по скриптам и в PEP 723.
На схеме в fetch.py выделен блок метаданных и показано, что uv run и обычный python делают с файлом.
uv может сам записать блок метаданных:
uv init --script fetch.py --python 3.12
Готовый скрипт выглядит так:
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx>=0.27,<1",
# ]
# ///
import httpx
response = httpx.get("https://example.com", timeout=10)
response.raise_for_status()
print(response.status_code)
Запускайте скрипт и редактируйте метаданные через uv:
uv run fetch.py
uv add --script fetch.py rich
uv remove --script fetch.py rich
Обычный python fetch.py игнорирует метаданные в комментариях. Поэтому скрипт зависит от совместимого раннера, хотя синтаксис Python остаётся корректным. uv — один из раннеров. pipx run, Hatch и PDM тоже читают этот блок.
Чтобы запускать скрипт как ./fetch.py, добавьте shebang, который вызывает uv:
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["httpx"]
# ///
import httpx
chmod +x fetch.py
./fetch.py
Флаг -S заставляет env разбить строку на отдельные аргументы, поэтому --script доходит до uv.
Для быстрого эксперимента передайте зависимости в командной строке, а не в метаданных. Вторая строка запускает Python REPL с доступным pandas:
uv run --with httpx scratch.py
uv run --with pandas python
Если скрипт должен воспроизводить резолвинг позже, создайте рядом с ним lock-файл:
uv lock --script fetch.py
uv run --script fetch.py
Эта команда записывает fetch.py.lock. Метка времени exclude-newer в блоке метаданных заставляет uv игнорировать версии пакетов, опубликованные после этой даты:
# /// script
# dependencies = ["httpx"]
# [tool.uv]
# exclude-newer = "2025-04-17T00:00:00Z"
# ///
Это слабее lock-файла. Метка не закрепляет точные версии и не может вернуть файл, который индекс позже удалил.
Используйте проект, когда несколько файлов делят зависимости, код импортируется как пакет, тестам нужно состояние проекта или несколько скриптов должны меняться вместе.
Существующие проекты с requirements.txt
Файл requirements.txt может содержать нестрогие входные требования, точные пины, хеши, ограничения, индексы, URL или экспорт, сгенерированный резолвером. Его воспроизводимость зависит от того, как его создали и как его используют. По одному имени файла этого не понять.
Есть два варианта: оставить формат pip и запускать его через uv pip или, для собственного приложения, мигрировать на uv.lock.
Для полностью разрешённого файла с прямыми и транзитивными зависимостями используйте pip-совместимый интерфейс uv без миграции:
uv venv
uv pip sync requirements.txt
.venv/bin/python app.py
Здесь запускайте установленный интерпретатор напрямую. Если есть pyproject.toml, uv run может перед запуском зафиксировать и синхронизировать его требования, заменив только что установленные пакеты. См. руководство uv по скриптам.
uv pip sync приводит окружение в соответствие с полностью разрешённым файлом. Если requirements.txt содержит только прямые требования, используйте uv pip install -r requirements.txt, чтобы разрешить и установить их зависимости. uv pip install -r добавляет пакеты. См. руководство uv по фиксации зависимостей в формате pip.
Для собственного приложения полезна поэтапная миграция:
uv init --bare
uv add -r requirements.in
uv add --group test -r requirements-test.in
uv lock
uv sync --locked
Перед удалением старых файлов прогоните полный путь тестов и деплоя. Если внешней системе всё ещё нужен формат pip, получайте его из проверенного lock-файла uv с помощью команды экспорта uv:
uv export --format requirements.txt \
--output-file requirements.txt
Не поддерживайте uv.lock и вручную отредактированный экспорт как два конкурирующих результата резолвинга.
Кэш и приватные индексы
Кэш uv ускоряет повторные установки, но остаётся одноразовым:
uv cache prune
uv cache clean
Для регулярной очистки предпочитайте prune. clean удаляет все записи кэша, и последующие установки снова скачивают и собирают пакеты.
Lock-файл повышает повторяемость. Он не делает зависимости заслуживающими доверия.
Для приватного индекса укажите его имя и URL в pyproject.toml, а учётные данные держите вне файла:
[[tool.uv.index]]
name = "internal"
url = "https://pypi.example.com/simple"
Передавайте учётные данные через UV_INDEX_INTERNAL_USERNAME и UV_INDEX_INTERNAL_PASSWORD или через keyring. uv строит имена переменных из имени индекса. См. документацию uv по аутентификации в индексах.
Старые команды и их аналоги в uv
Если вы переходите со старых инструментов, эта таблица сопоставляет каждой знакомой команде команду uv, которая делает то же самое.
| Задача | Раньше | uv |
|---|---|---|
| Установить Python | pyenv install 3.12 | uv python install 3.12 |
| Новый проект и окружение | mkdir proj && cd proj && python -m venv .venv | uv init proj && cd proj && uv sync |
| Добавить пакет | pip install pandas && pip freeze > requirements.txt | uv add pandas |
| Скомпилировать пины | pip-compile requirements.in | uv lock |
| Обновить один пакет | pip-compile --upgrade-package httpx | uv lock --upgrade-package httpx |
| Запустить в окружении | source .venv/bin/activate && python app.py | uv run app.py |
| Скрипт с зависимостями | отдельный venv вручную для одного файла | блок PEP 723 + uv run |
| Запустить инструмент разово | pipx run black | uvx black |
| Установить инструмент | pipx install ruff | uv tool install ruff |
| Воспроизводимая установка в CI | pip install -r requirements.txt | uv sync --locked |
Короткий рабочий чек-лист
Для каждого проекта:
- Задайте
requires-pythonи прямые зависимости вpyproject.toml. - Разделяйте публикуемые extras (
--optional) и локальные группы зависимостей (--group). - Коммитьте
uv.lock. Игнорируйте.venvи состояние кэша. - Запускайте инструменты, связанные с проектом, через окружение проекта.
- Используйте в CI
uv lock --checkили--locked. - Тестируйте на каждой целевой ОС и архитектуре, которые покрывает lock-файл.
- Экспортируйте совместимые форматы только для конкретных внешних потребителей.
- Держите учётные данные индексов вне коммитируемых файлов. Передавайте их через переменные окружения или keyring.
На macOS uv — мой основной инструмент для Python: один исполняемый файл покрывает эти задачи и держит зависимости проектов и инструментов раздельно.