uv на macOS: управление версиями Python, проектами и инструментами
Автоматический перевод Эта статья была автоматически переведена с оригинальной английской версии.
Я перевёл свой Python-воркфлоу на uv. Он заменил переключение между pip, виртуальными окружениями, pip-tools, pipx и менеджерами проектов, которым я раньше занимался вручную. Теперь один исполняемый файл покрывает большую часть этих задач.
При этом сами воркфлоу остаются разными. Для Python-разработчиков, переходящих на uv в macOS с pip, виртуальных окружений, pip-tools или pipx, проект, скрипт с inline-метаданными, разовый CLI-инструмент и установленный CLI-инструмент относятся к разным окружениям и жизненным циклам. Ниже показано, как выбирать между ними.
Быстрый старт
# Install uv with Homebrew
brew install uv
# Start a project
uv init example-app
cd example-app
uv add httpx
uv run python main.py
# Run an isolated CLI without installing it permanently
uvx ruff check .
Коротко. В проектах я использую
uv add,uv lock,uv syncиuv run. Для самодостаточных скриптов использую метаданные PEP 723, для разовых инструментов —uvx, а для команд, которые должны оставаться доступными вPATH, —uv tool install. В CI--lockedпроверяет, что метаданные проекта согласуются сuv.lock.--frozenдоверяет существующему lock-файлу и не проверяет его актуальность.
Установка uv с одним владельцем
Homebrew — удобный способ установить uv в macOS. Поддерживаемые варианты перечислены в руководстве по установке uv:
brew install uv
uv --version
Если uv установил Homebrew, обновлять его тоже должен Homebrew:
brew upgrade uv
uv self update предназначен для автономного способа установки uv и отключён для установок через менеджеры пакетов. Не допускайте, чтобы два установщика конкурировали за один и тот же исполняемый файл.
Полезные команды для проверки окружения:
command -v uv
uv python dir
uv tool dir
uv cache dir
У управляемых установок Python, постоянных инструментов и одноразовых записей в кэше разные каталоги. Кэш можно удалить и пересоздать. Он не является источником истины.
Проекты: объявления, резолвинг и окружение
У проекта uv обычно есть три артефакта:
pyproject.tomlсодержит метаданные проекта и прямые зависимости.uv.lockхранит кроссплатформенный результат резолвинга uv..venv— локальное установленное окружение, которое должно быть одноразовым.
Создайте приложение и добавьте зависимости для рантайма и тестов:
uv init forecast-app
cd forecast-app
uv add httpx
uv add --group test pytest
uv run python main.py
uv run --group test pytest
Актуальные шаблоны приложений uv создают main.py, pyproject.toml, README.md и .python-version. По умолчанию они не задают build-систему. Для упаковываемой библиотеки с layout src и build-бэкендом используйте uv init --lib.
uv run проверяет проект, при необходимости обновляет lock-файл, синхронизирует обязательные зависимости и запускает команду. Подробнее об этом жизненном цикле см. в документации uv по структуре проекта и синхронизации. Первая операция над проектом при необходимости создаёт .venv и uv.lock. Сама команда uv init создаёт только файлы проекта.
Коммитьте входные данные, а не окружение
Коммитьте pyproject.toml, uv.lock, исходный код и осознанно выбранный .python-version. Добавьте в ignore .venv и кэши uv.
Lock-файл фиксирует резолвинг с учётом поддерживаемых маркеров и платформ. Он не делает native wheels идентичными в macOS, Linux, на Intel и Apple Silicon. Тестируйте каждую платформу деплоя.
Актуальность 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-гейт.
Используйте --frozen только тогда, когда намеренно хотите, чтобы uv применил существующий lock-файл без проверки его актуальности:
uv sync --frozen
Это может быть полезно на контролируемом этапе сборки, если lock-файл уже прошёл валидацию, но такая команда не обнаруживает устаревшие lock-файлы.
Точность — отдельная настройка, не связанная с актуальностью. uv sync по умолчанию работает точно и удаляет лишние пакеты. 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 и другие совместимые инструменты. requires-python в pyproject.toml — контракт совместимости проекта. Делайте оба параметра осознанными:
[project]
requires-python = ">=3.12,<3.14"
Фиксация 3.12 не гарантирует одну и ту же patch-сборку навсегда или один и тот же артефакт во всех операционных системах. Когда нужна конкретная patch-версия, запрашивайте её явно, а в CI явно выбирайте и выводите информацию об интерпретаторе.
Используйте другой менеджер Python, если проекту нужен дистрибутив или конфигурация сборки, которых uv не предоставляет. При этом uv всё равно может использовать такой интерпретатор через --python или обычное обнаружение.
Выбор между uv run, uvx и установленными инструментами
Инструменты, связанные с проектом: uv run
Если pytest, mypy, генератор кода или другой инструмент должен импортировать проект либо использовать его заблокированные плагины, объявите его в группе зависимостей:
uv add --group lint ruff
uv run --group lint ruff check .
Запуск такого инструмента через uvx изолирует его от проекта и может скрыть установленный пакет или нужные ему плагины.
Разовые инструменты: uvx
uvx — это псевдоним для uv tool run. Он создаёт изолированное окружение, хранящееся в одноразовом кэше uv. В документации uv по инструментам описаны этот кэш и жизненный цикл постоянных инструментов:
uvx ruff@0.12.0 check .
uvx --from jupyterlab jupyter lab
uvx --with mkdocs-material mkdocs --help
Фиксируйте версию инструмента в CI или документации. Первый вызов без версии выбирает актуальный релиз, а последующие вызовы могут повторно использовать состояние кэша.
Постоянные исполняемые файлы: uv tool install
Устанавливайте инструмент, если скриптам вне вашего контроля нужна его команда в PATH или если манифест конфигурации машины должен владеть этой установкой:
uv tool install ruff
uv tool list
uv tool upgrade ruff
uv tool uninstall ruff
Постоянные инструменты по-прежнему используют изолированные окружения. Не изменяйте эти окружения вручную через pip.
Скрипты: один файл как единица поставки
PEP 723 определяет inline-метаданные скрипта. Совместимый раннер может прочитать блок комментариев и создать изолированное окружение. Формат метаданных и поведение раннера описаны в руководстве uv по скриптам и PEP 723.
# /// 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 остаётся корректным.
Если скрипт должен позднее воспроизводить тот же результат резолвинга, создайте рядом с ним lock-файл:
uv lock --script fetch.py
uv run --script fetch.py
Будет создан fetch.py.lock. Метка времени exclude-newer может ограничить даты дистрибутивов-кандидатов, но это слабее точного заблокированного результата резолвинга и не гарантирует, что артефакт останется доступным.
Используйте проект, если несколько файлов используют общие зависимости, код импортируется как пакет, тестам требуется состояние проекта или несколько скриптов должны поставляться вместе.
Файлы requirements: совместимость, а не гарантия
Файл requirements.txt может содержать неточные входные данные, точные pin-версии, хэши, ограничения, индексы, URL или экспорт requirements, созданный резолвером. Воспроизводимость зависит от способа его создания и использования. Одно имя файла ничего не говорит.
Используйте совместимый с pip интерфейс uv без миграции:
uv venv
uv pip sync requirements.txt
uv run python app.py
uv pip sync приводит окружение в соответствие с файлом. uv pip install -r добавляет зависимости, не удаляя уже установленные.
Для приложения, которым вы владеете, может быть полезна поэтапная миграция:
uv init --bare
uv add -r requirements.in
uv add --group test -r requirements-test.in
uv lock
uv sync --locked
Перед удалением старых файлов выполните полный путь тестирования и деплоя. Если downstream-системе по-прежнему нужен формат pip, получите его из провалидированного lock-файла uv с помощью команды экспорта uv:
uv export --format requirements.txt \
--output-file requirements.txt
Не поддерживайте uv.lock и экспорт, отредактированный вручную, как два конкурирующих результата резолвинга.
Кэш, индексы и границы supply chain
Кэш uv ускоряет повторные установки, но остаётся одноразовым:
uv cache prune
uv cache clean
Для обычной очистки предпочтительно использовать prune. clean удаляет все записи кэша и заставляет последующие команды заново скачивать и собирать пакеты.
Lock-файл повышает воспроизводимость. Но он не делает зависимости надёжными. Проверяйте источники пакетов, конфигурацию индексов, Git-ревизии, build-бэкенды, лицензии и учётные данные. Не храните конфигурацию аутентифицированных индексов в коммитах. Исключение — несекретная ссылка на одобренный механизм получения учётных данных.
Для native-пакетов фиксируйте архитектуру деплоя и проверяйте доступность wheel-файлов. Иначе резолвер может перейти к сборке из исходников, для которой нужны компиляторы и системные библиотеки, отсутствующие в CI или продакшене.
Краткий операционный чек-лист
Для каждого проекта:
- Задайте
requires-pythonи прямые зависимости вpyproject.toml. - Отделяйте публикуемые extras от локальных групп зависимостей.
- Коммитьте
uv.lock. Добавьте в ignore.venvи состояние кэша. - Запускайте связанные с проектом инструменты через окружение проекта.
- Используйте в CI
uv lock --checkили--locked. - Тестируйте каждую целевую ОС и архитектуру, представленную в lock-файле.
- Экспортируйте совместимые форматы только для явно указанных downstream-потребителей.
- Проверяйте политику источников и учётных данных независимо от резолвинга.
uv наиболее полезен, когда эти границы владения остаются видимыми. Один бинарник может управлять ими всеми, не превращая их в одно окружение. Инструментов становится меньше, а четыре воркфлоу остаются раздельными. Поэтому uv по-прежнему остаётся моим основным инструментом для Python-проектов в macOS.