uv — 快速 Python 包与项目管理器 是做什么的?
概述
uv 是 Astral 提供的一个单一二进制文件,覆盖了完整的 Python 工作流程:带有跨平台锁文件 (uv.lock) 的项目、带有内联依赖的单个脚本、命令行工具 (uvx)、Python 解释器安装以及一个即插即用的 uv pip 接口。它能在需要时自动下载 Python 解释器,因此机器上只需安装 uv 即可。
使用说明
安装
# 包管理器
pipx install uv
brew install uv
winget install --id=astral-sh.uv -e
Astral 还为没有包管理器的机器提供了一个独立安装程序;安装页面 https://docs.astral.sh/uv/getting-started/installation/ 包含了当前的命令和校验和。
uv --version
uv self update # 仅适用于独立安装程序;否则使用包管理器升级
要锁定某个版本,请在安装程序 URL 中指定版本:https://astral.sh/uv/0.12.21/install.sh。
创建项目
uv init dispatch-api # 打包应用:src/dispatch_api/,uv_build 后端,console 脚本
uv init --no-package cron-jobs # 扁平布局,包含 main.py,无构建系统
uv init --lib geo-utils # 库,带有 py.typed 标记
uv init --bare --python 3.12 # 仅在当前目录下生成 pyproject.toml
uv init 还会写入 .python-version、README.md、.gitignore 并初始化 git。--bare 仅写入 pyproject.toml,因此在现有仓库内使用它是正确的选择。
管理依赖
uv add 'httpx>=0.27' 'pydantic>=2.7,<3' # 运行时依赖
uv add --dev pytest ruff # dev 组,不发布到生产环境
uv add --group docs mkdocs # 任意命名的依赖组
uv add --optional postgres 'psycopg[binary]' # 额外依赖:pip install dispatch-api[postgres]
uv add -r requirements.txt # 导入现有的 requirements 文件
uv remove httpx
uv lock --upgrade-package pydantic # 在其约束范围内更新一个包
uv lock --upgrade # 更新所有包
uv tree --outdated --depth 1 # 查看有哪些包的更新版本
第一次使用 uv add 时会创建 .venv/ 和 uv.lock。每次更改都会同时更新 pyproject.toml、锁文件和环境。
运行和同步
uv run pytest # 如果需要则锁定并同步,然后在 .venv 内运行
uv run python -m dispatch_api
uv run --with rich python -c "import rich" # 仅在此命令中额外使用一个包
uv sync # 安装所有依赖,包括 dev 组
uv sync --locked # CI:如果 uv.lock 与 pyproject.toml 不匹配则失败
uv sync --frozen --no-dev # 生产环境:原样使用 uv.lock,跳过 dev 组
uv sync --extra postgres
uv lock --check # 当锁文件过期时返回非零退出码
无需激活环境;uv run 会自动找到项目环境。
带有内联依赖的脚本
uv init --script fetch-rates.py --python 3.12
uv add --script fetch-rates.py 'httpx<1' rich
uv run fetch-rates.py
uv lock --script fetch-rates.py # 写入 fetch-rates.py.lock 以确保可重复运行
uv add --script 会在文件顶部维护一个元数据块:
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx<1",
# "rich>=15.0.0",
# ]
# ///
将 #!/usr/bin/env -S uv run --script 作为文件的第一行,然后 chmod +x 该文件即可直接运行。
工具
uvx ruff check . # 无需安装直接运行;uvx = uv tool run
uvx [email protected] check . # 指定精确版本
uvx --from 'markitdown[pdf]' markitdown q3-board-report.pdf # 命令名与包名不同
uv tool install ruff # 永久隔离安装到 PATH
uv tool upgrade --all
uv tool list
uv tool uninstall ruff
已安装的工具会放置到 uv tool dir --bin 打印的目录中;如果该目录不在 PATH 上,uv 会发出警告。
Python 版本
uv python install 3.12 3.13
uv python list --only-installed
uv python pin 3.12 # 写入 .python-version
uv run --python 3.13 pytest # 在另一个解释器上一次性运行
uv python upgrade 3.12 # 3.12 的最新补丁版本
uv venv --python 3.12 # 在 .venv 创建普通虚拟环境
请求可以接受 3.12、3.12.3、>=3.11,<3.13、[email protected] 和 3.13t(自由线程)。uv 会自动下载缺失的解释器;传递 --no-python-downloads 可以禁止此行为。
pip 兼容接口
uv venv
uv pip install -r requirements.txt
uv pip compile requirements.in -o requirements.txt --universal
uv pip sync requirements.txt # 移除未列出的任何内容
uv pip install --python .venv/bin/python 'rich>=13'
CI 和 Docker
name: ci
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
version: "0.12.21"
enable-cache: true
- run: uv python install
- run: uv sync --locked --all-extras --dev
- run: uv run pytest
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:0.12.21 /uv /uvx /bin/
WORKDIR /app
ENV UV_NO_DEV=1 UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
# 先处理依赖,这样只有在 uv.lock 更改时才需要重新构建这一层
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv uv sync --locked
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "dispatch_api"]
将 .venv 添加到 .dockerignore 文件;主机的环境在镜像内无法正常工作。
示例
示例 1:将使用 requirements.txt 的服务迁移到带锁文件的 uv 项目
需求: “我们的 dispatch-api 仓库有 requirements.txt 和 requirements-dev.txt。将其迁移到 uv 并提供锁文件,让测试能运行。”
cd dispatch-api
uv init --bare --python 3.12
uv python pin 3.12
uv add -r requirements.txt
sed '/^-r /d' requirements-dev.txt | uv add --dev -r -
uv run python -m pytest -q
uv run ruff check .
结果: pyproject.toml 列出了五个运行时包和一个 dev 组,uv.lock 锁定了 30 个包,检查通过。简化输出:
Initialized project `dispatch-api`
Pinned `.python-version` to `3.12`
+ uvicorn==0.34.0
+ pytest==8.3.4
+ ruff==0.8.6
1 passed, 1 warning in 0.12s
All checks passed!
示例 2:自包含脚本
需求: “编写一个磁盘使用报告脚本,可以复制到任何服务器上直接运行,无需设置环境。”
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = ["rich>=13"]
# ///
import shutil
from rich.console import Console
from rich.table import Table
usage = shutil.disk_usage("/")
table = Table(title="磁盘使用情况")
table.add_column("总 GB")
table.add_column("空闲 GB")
table.add_row(f"{usage.total / 1e9:.0f}", f"{usage.free / 1e9:.0f}")
Console().print(table)
chmod +x disk-report.py
./disk-report.py
结果: uv 在首次运行时构建一个包含 rich 的缓存环境并打印表格;后续运行立即启动。
Disk usage
┏━━━━━━━━━━┳━━━━━━━━━┓
┃ Total GB ┃ Free GB ┃
┡━━━━━━━━━━╇━━━━━━━━━┩
│ 3936 │ 2211 │
└──────────┴─────────┘
使用指南
- 提交
uv.lock和.python-version。 永远不要手动编辑uv.lock;通过修改pyproject.toml或使用uv add、uv remove和uv lock来管理。 --locked与--frozen:--locked验证锁文件,如果过期则失败,适合 CI。--frozen跳过检查,直接安装文件上指定的内容。uv sync是精确的,uv run不是。uv sync会移除不在锁文件中的包,包括通过uv pip install添加的任何包。uv run只添加缺失的内容。uv pip通过自动发现选择目标: 会使用活动的VIRTUAL_ENV,然后是当前目录或其最近父目录中的.venv。如果在错误的文件夹中运行,可能会安装到其他环境中。在脚本中,请使用--python指定环境路径。- 扁平项目与 pytest: 使用
--bare或--no-package时,项目本身不会被安装,因此uv run pytest可能会因为ModuleNotFoundError而失败。请使用uv run python -m pytest,或在[tool.pytest.ini_options]下设置pythonpath = ["."]。 uv init默认创建打包的src/布局。 对于脚本式项目(包含main.py),请传递--no-package。- 实验性命令:
uv format、uv check和uv audit会打印一个警告,表示它们可能会发生变化。暂时不要在此基础上构建流水线。 - 私有索引: 在
[[tool.uv.index]]下声明它们,并通过以索引名称命名环境变量来传递凭据:对于名为internal的索引,使用UV_INDEX_INTERNAL_USERNAME和UV_INDEX_INTERNAL_PASSWORD。切勿提交它们。默认的first-index策略可防止依赖混淆攻击;请避免使用unsafe-best-match。 - 供应链安全:
uv run、uvx和带有内联元数据的脚本会下载并执行包。运行来自不受信任来源的任何脚本之前,请先审查其依赖列表。 uv self update仅适用于通过独立安装程序进行的安装。- CI 缓存: 在保存缓存之前运行
uv cache prune --ci以保持缓存较小。 - 何时不使用: uv 从 Python 包索引、git 和本地路径安装。它不安装 conda 包或系统库,因此对于依赖这些的栈,请继续使用 conda。