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 и области хранения: .venv проекта рядом с pyproject.toml и uv.lock, окружения скриптов и uvx в одноразовом кэше uv, окружение установленного инструмента в каталоге инструментов, которое uvx может использовать повторно, и управляемые установки Python для всех четырёх. Для установленных команд каталог исполняемых файлов должен быть в PATHЧетыре варианта работы с uv и области хранения: .venv проекта рядом с pyproject.toml и uv.lock, окружения скриптов и uvx в одноразовом кэше uv, окружение установленного инструмента в каталоге инструментов, которое uvx может использовать повторно, и управляемые установки Python для всех четырёх. Для установленных команд каталог исполняемых файлов должен быть в PATH

Установка и обновление 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.

Вы редактируете pyproject.toml, .python-version и исходный код; uv lock пишет uv.lock; все четыре коммитятся. uv sync собирает .venv из uv.lock, пакеты клонируются из кэша uv, а окружение и кэш остаются вне gitВы редактируете pyproject.toml, .python-version и исходный код; uv lock пишет uv.lock; все четыре коммитятся. uv sync собирает .venv из uv.lock, пакеты клонируются из кэша uv, а окружение и кэш остаются вне 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 не обновили.

rich есть в pyproject.toml, но нет в uv.lock: uv sync переписывает lock и ставит rich, uv sync --locked и uv lock --check завершаются с ошибкой, а uv sync --frozen ставит httpx без rich, и CI остаётся зелёнымrich есть в pyproject.toml, но нет в uv.lock: uv sync переписывает lock и ставит rich, uv sync --locked и uv lock --check завершаются с ошибкой, а uv sync --frozen ставит httpx без rich, и CI остаётся зелёным

Точность — отдельная настройка, не связанная с актуальностью. uv sync по умолчанию работает в точном режиме и удаляет лишние пакеты. uv run по умолчанию выполняет неточную синхронизацию, если не указан --exact. На схеме показано, что происходит с пакетом, установленным вручную, при uv sync, uv run и uv run --exact.

.venv с httpx, ipykernel и установленным вручную extra-pkg: uv sync и uv run --exact удаляют extra-pkg, uv run его оставляет.venv с httpx, ipykernel и установленным вручную extra-pkg: uv sync и uv run --exact удаляют extra-pkg, uv run его оставляет

Установка и закрепление версий 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 использует .venv проекта и его зафиксированные зависимости; uvx использует отдельное окружение инструмента из кэша или установленного инструмента, не устанавливая зависимости проекта автоматически; uv tool install сохраняет окружение в каталоге инструментов и делает команду доступной после настройки PATHuv run использует .venv проекта и его зафиксированные зависимости; uvx использует отдельное окружение инструмента из кэша или установленного инструмента, не устанавливая зависимости проекта автоматически; uv tool install сохраняет окружение в каталоге инструментов и делает команду доступной после настройки 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 делают с файлом.

fetch.py с выделенным блоком комментариев PEP 723: uv run читает блок, находит Python 3.12 или новее, ставит httpx в изолированное окружение и запускает код; python fetch.py запускает только кодfetch.py с выделенным блоком комментариев PEP 723: uv run читает блок, находит Python 3.12 или новее, ставит httpx в изолированное окружение и запускает код; python fetch.py запускает только код

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
Установить Pythonpyenv install 3.12uv python install 3.12
Новый проект и окружениеmkdir proj && cd proj && python -m venv .venvuv init proj && cd proj && uv sync
Добавить пакетpip install pandas && pip freeze > requirements.txtuv add pandas
Скомпилировать пиныpip-compile requirements.inuv lock
Обновить один пакетpip-compile --upgrade-package httpxuv lock --upgrade-package httpx
Запустить в окруженииsource .venv/bin/activate && python app.pyuv run app.py
Скрипт с зависимостямиотдельный venv вручную для одного файлаблок PEP 723 + uv run
Запустить инструмент разовоpipx run blackuvx black
Установить инструментpipx install ruffuv tool install ruff
Воспроизводимая установка в CIpip install -r requirements.txtuv sync --locked

Короткий рабочий чек-лист

Для каждого проекта:

  1. Задайте requires-python и прямые зависимости в pyproject.toml.
  2. Разделяйте публикуемые extras (--optional) и локальные группы зависимостей (--group).
  3. Коммитьте uv.lock. Игнорируйте .venv и состояние кэша.
  4. Запускайте инструменты, связанные с проектом, через окружение проекта.
  5. Используйте в CI uv lock --check или --locked.
  6. Тестируйте на каждой целевой ОС и архитектуре, которые покрывает lock-файл.
  7. Экспортируйте совместимые форматы только для конкретных внешних потребителей.
  8. Держите учётные данные индексов вне коммитируемых файлов. Передавайте их через переменные окружения или keyring.

На macOS uv — мой основной инструмент для Python: один исполняемый файл покрывает эти задачи и держит зависимости проектов и инструментов раздельно.

Ссылки