文章

Python 包管理:uv

Python 包管理:uv

Python 项目管理长期以来依赖一组分散工具:pip 负责安装包,venvvirtualenv 负责虚拟环境,pip-tools 负责锁定依赖,pipx 负责安装命令行工具,pyenv 负责管理 Python 版本,poetrypdm 负责项目级依赖管理。

uv 把这些功能尽量收敛到一个命令行工具里。它由 Astral 开发,使用 Rust 编写,定位是一个高速的 Python 包与项目管理器。

实际使用中,uv 最值得关注的不是“快”本身,而是它把 Python 开发中最常见的几个动作统一成了一套一致的工作流:创建项目、声明依赖、生成锁文件、同步虚拟环境、运行脚本、安装 Python 解释器、管理全局 CLI 工具,以及构建和发布包。


uv 的两种工作模式

第一种是项目管理模式。这是更推荐的现代工作流。项目依赖写在 pyproject.toml 中,解析结果写入 uv.lock,本地环境通常是项目目录下的 .venv。开发者通过 uv adduv removeuv lockuv syncuv run 来管理项目。

第二种是pip 兼容模式。它更像传统的 pippip-toolsvirtualenv 工作流。开发者可以继续使用 requirements.txt,并用 uv pip installuv pip compileuv pip sync 等命令替代原来的工具链。

如果是新项目,优先使用项目管理模式。如果是老项目、CI 脚本或已有 requirements.txt 的项目,可以先从 pip 兼容模式迁移。

安装 uv

macOS 和 Linux 可以使用官方安装脚本:

1
curl -LsSf https://astral.sh/uv/install.sh | sh

Windows PowerShell:

1
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

也可以通过包管理器安装:

1
2
3
brew install uv
pipx install uv
pip install uv

安装后检查版本:

1
uv --version

如果你是通过 standalone installer 安装的 uv,可以用下面的命令升级 uv 自身:

1
uv self update

缓存与目录管理

uv 的速度很大程度来自缓存机制。它会缓存下载过的包、构建过的 wheel、源码包以及工具运行所需的环境。理解这些目录的位置,有助于排查磁盘占用、配置 CI 缓存,以及区分“项目环境”和“全局工具环境”。

查看 uv 的缓存目录

查看当前缓存目录:

1
uv cache dir

默认情况下,缓存目录通常位于:

系统默认缓存目录
Linux~/.cache/uv$XDG_CACHE_HOME/uv
macOS~/Library/Caches/uv
Windows%LOCALAPPDATA%\uv\cache

这个目录主要用于存放 uv 的包缓存,包括下载的 distributions、构建产物、源码包、wheel 等。也就是说,uv pip installuv syncuv adduvx 等命令复用依赖时,都会受益于这个缓存。

需要注意,不建议手动删除缓存目录里的局部文件。官方建议使用 uv 自带命令管理缓存:

1
uv cache clean

如果只想清理某个包的缓存,可以使用:

1
uv cache clean requests

也可以删除当前未被使用的缓存项:

1
uv cache prune

修改缓存目录

临时指定某次命令使用的缓存目录:

1
uv pip install requests --cache-dir /path/to/uv-cache

长期修改缓存目录,建议设置环境变量:

1
export UV_CACHE_DIR=/path/to/uv-cache

Windows PowerShell 示例:

1
[Environment]::SetEnvironmentVariable("UV_CACHE_DIR", "D:\uv-cache", "User")

在 CI 中,也可以把 UV_CACHE_DIR 指向一个可缓存路径,例如:

1
export UV_CACHE_DIR="$HOME/.cache/uv"

然后让 CI 系统缓存这个目录,从而减少重复下载和构建时间。

uv 管理的 Python 版本目录

如果使用:

1
uv python install 3.12

uv 会下载并管理对应的 Python 解释器。查看 uv 管理的 Python 安装目录:

1
uv python dir

默认情况下,uv 管理的 Python 版本会放在持久数据目录的 python/ 子目录中。例如在 Linux 上通常类似:

1
~/.local/share/uv/python

查看 uv 管理的 Python 可执行文件目录:

1
uv python dir --bin

如果你希望修改 uv 管理 Python 的安装位置,可以使用环境变量:

1
export UV_PYTHON_INSTALL_DIR=/path/to/uv-python

uv tool install 的工具目录

uv tool install 用来安装全局可执行工具,例如:

1
uv tool install ruff

这些工具不会安装到某个项目的 .venv 中,而是安装到 uv 的工具目录里。查看工具安装目录:

1
uv tool dir

默认情况下,工具环境位于 uv 持久数据目录的 tools/ 子目录中。例如 Linux 上通常类似:

1
~/.local/share/uv/tools

查看工具可执行文件目录:

1
uv tool dir --bin

这个目录里会放置可直接调用的命令,例如 ruffblackmypy 等。使用 standalone installer 安装 uv 时,uvuvx 这两个可执行文件也会安装到对应的可执行文件目录中。

如果要修改工具环境目录,可以设置:

1
export UV_TOOL_DIR=/path/to/uv-tools

如果要修改工具可执行文件目录,可以设置:

1
export UV_TOOL_BIN_DIR=/path/to/bin

uvx 的临时工具环境

uvxuv tool run 的简写:

1
uvx ruff check .

它会在隔离环境中运行命令行工具。与 uv tool install 不同,uvx 更偏向“临时执行”,适合偶尔运行某个工具,而不是把工具长期安装到系统路径中。

例如:

1
2
3
uvx pycowsay "hello uv"
uvx ruff check .
uvx black src/ --check

uvx 运行工具时会复用 uv 的缓存,因此第二次运行通常会更快。但它不会像 uv tool install ruff 那样,把 ruff 长期安装成一个稳定的全局命令。

新项目

创建一个新项目:

1
2
uv init my-app
cd my-app

uv init 会创建一个符合 pyproject.toml 规范的项目。某些文件和状态不一定会立即生成。例如,.venvuv.lock 通常是在首次同步环境时才创建。

添加依赖:

1
uv add requests

这条命令会做几件事:

  1. requests 写入 pyproject.toml
  2. 解析依赖版本;
  3. 更新 uv.lock
  4. 同步项目虚拟环境。

添加开发依赖:

1
uv add --dev pytest ruff

移除依赖:

1
uv remove requests

查看依赖树:

1
uv tree

手动生成或更新锁文件:

1
uv lock

同步本地环境:

1
uv sync

uv sync 的作用是让本地虚拟环境与 uv.lock 保持一致。默认情况下,它会移除 lockfile 中不存在的多余包。因此,它非常适合团队协作、CI 环境和生产部署前的环境对齐。

如果你希望保留环境里额外安装的包,可以使用:

1
uv sync --inexact

运行代码:优先使用 uv run

在项目里运行脚本:

1
uv run python main.py

或直接运行 Python 文件:

1
uv run main.py

uv run 会确保命令运行在合适的 Python 环境中。在项目目录中,它会先检查项目环境是否需要创建或更新,然后再执行命令。

这意味着你通常不需要手动执行:

1
source .venv/bin/activate

在日常开发中,可以直接使用:

1
2
3
uv run pytest
uv run ruff check .
uv run python scripts/report.py

需要注意的是,uv run 默认会确保所需依赖存在,但不会像 uv sync 那样默认删除环境中的多余包。如果你希望运行前也进行严格同步,可以使用:

1
uv run --exact python main.py

单文件脚本:用 PEP 723 声明内联依赖

uv 对单文件脚本也很友好。假设你有一个 script.py,文件头部声明了内联依赖:

1
2
3
4
5
6
7
8
9
# /// script
# dependencies = [
#   "requests",
# ]
# ///

import requests

print(requests.get("https://example.com").status_code)

运行:

1
uv run script.py

uv 会读取脚本中的依赖元数据,并在隔离的临时环境中安装依赖后执行脚本。这个模式非常适合写一次性的自动化脚本、数据处理脚本或分享给同事的小工具。

也可以让 uv 帮你把依赖写入脚本元数据:

1
uv add --script script.py requests

管理 Python 版本

查看可用 Python 版本:

1
uv python list

安装指定 Python 版本:

1
uv python install 3.12

在当前项目中固定 Python 版本:

1
uv python pin 3.12

这会在项目中写入 .python-version,后续 uv 命令会优先使用这个版本。

如果你只是想为某个项目指定 Python 版本,可以这样做:

1
2
3
uv python install 3.12
uv python pin 3.12
uv sync

这能覆盖很多原本需要 pyenv 的场景。不过,如果你依赖复杂的 shell 集成、全局 Python shim 或系统级 Python 版本切换,仍然需要根据团队环境判断是否完全替换 pyenv

管理命令行工具:替代 pipx

很多 Python 工具是命令行程序,例如 ruffblackmypyhttpie。这些工具不一定应该安装进某个业务项目的 .venv 中。

临时运行一个工具:

1
uvx ruff check .

uvxuv tool run 的简写,适合一次性运行工具。比如你没有提前安装 ruff,也可以直接执行。

指定工具版本:

1
2
uvx ruff@latest check .
uvx ruff@0.6.0 check .

长期安装一个全局工具:

1
uv tool install ruff

查看已安装工具:

1
uv tool list

升级工具:

1
uv tool upgrade ruff

卸载工具:

1
uv tool uninstall ruff

这个模式比把工具装进全局 Python 环境更干净,也避免污染具体项目的虚拟环境。

pip 兼容模式

如果你暂时不想迁移到 pyproject.toml,也可以继续使用 requirements.txt

创建虚拟环境:

1
uv venv

指定 Python 版本创建虚拟环境:

1
uv venv --python 3.11

安装依赖:

1
uv pip install -r requirements.txt

安装单个包:

1
uv pip install requests

把宽松依赖锁定成 requirements.txt

1
uv pip compile requirements.in -o requirements.txt

严格同步环境:

1
uv pip sync requirements.txt

这套命令适合旧项目、Dockerfile、CI 以及暂时不想引入 pyproject.toml 的仓库。

升级依赖

uv.lock 的目的不是“永远自动追最新”,而是保证可复现。也就是说,即使 PyPI 上发布了新版本,只要当前锁文件仍满足 pyproject.toml 中的版本约束,uv 不会自动升级锁定版本

升级所有依赖:

1
2
uv lock --upgrade
uv sync

升级单个依赖:

1
2
uv lock --upgrade-package requests
uv sync

升级到指定版本:

1
2
uv lock --upgrade-package requests==2.32.3
uv sync

也可以直接在同步时升级:

1
uv sync --upgrade-package requests

如果你修改的是依赖约束本身,可以使用:

1
uv add "requests>=2.32"

这会更新 pyproject.toml 中的依赖声明。需要注意,锁定版本是否变化,取决于新约束是否排除了原来的锁定版本,或者你是否显式要求升级。

构建与发布 Python 包

如果你的项目是一个可发布的 Python 包,可以使用:

1
uv build

构建结果默认输出到 dist/ 目录,通常包括源码包和 wheel。

发布到包仓库:

1
uv publish

发布前建议确认项目的 pyproject.toml 中已经正确配置了包名、版本、构建系统、README、license 等元数据。

常见发布流程如下:

1
2
3
4
uv lock
uv sync
uv build
uv publish

如果你要发布给 PyPI,建议提前确认认证方式,例如 token、环境变量或 uv 的认证配置。

常用命令速查

场景命令
创建项目uv init my-app
添加运行时依赖uv add requests
添加开发依赖uv add --dev pytest ruff
移除依赖uv remove requests
生成或更新锁文件uv lock
严格同步环境uv sync
运行脚本uv run python main.py
查看依赖树uv tree
安装 Pythonuv python install 3.12
固定项目 Python 版本uv python pin 3.12
创建虚拟环境uv venv
pip 兼容安装uv pip install -r requirements.txt
编译 requirementsuv pip compile requirements.in -o requirements.txt
同步 requirements 环境uv pip sync requirements.txt
临时运行工具uvx ruff check .
全局安装工具uv tool install ruff
构建包uv build
发布包uv publish
清理缓存uv cache clean
升级 uvuv self update

推荐工作流

新项目推荐这样开始:

1
2
3
4
5
6
uv init my-app
cd my-app
uv python pin 3.12
uv add requests
uv add --dev pytest ruff
uv run python main.py

团队协作时,把这些文件提交到 Git:

1
2
3
pyproject.toml
uv.lock
.python-version

一般不要提交:

1
.venv/

其他成员拉取代码后执行:

1
uv sync

即可得到与锁文件一致的开发环境。

warning

本文由作者按照 CC BY 4.0 进行授权