Files
cs-note/hzh/DEV/UV.md
T
2026-05-24 11:42:38 +08:00

357 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [uv, python, package-manager, virtualenv, dependency-management]
create time: 2026-05-05 15:30
---
# UV — Fast Python Package Manager
## 概述
UV 是 Astral(Ruff 的出品方)开发的极速 Python 包管理工具,用 Rust 编写。它同时承担 **包安装器**、**虚拟环境管理器** 和 **解析器** 三种角色,兼容 `pip`/`venv`/`virtualenv` 的工作流,但在性能上实现了数量级提升。
> [!tip] 为什么选择 UV?
> pip + venv + virtualenv + poetry 需要多个工具协作——每个工具职责不同、配置分散。UV 将安装包、管理虚拟环境、生成 lock 文件、管理多版本项目统一到一个 CLI 中。基准测试显示,`uv pip install` 比 pip 快 **10~100 倍**。
> [!question] 思考
> 一个包管理工具为什么要重写为 Rust?
> —— Python 生态的依赖解析和下载是 I/O 密集型任务。Rust 提供了零成本抽象和极低的运行时开销,配合并发下载能力,这正是 pip(单线程 CPython 实现)难以企及的。
## 核心概念
### UV 与 pip/venv/poetry 的关系
```mermaid
graph TD
subgraph Traditional["传统工具链"]
P[pip — 包安装]
V[venv/virtualenv — 虚拟环境]
PO[poetry/pip-tools — 依赖锁定]
end
subgraph UVChain["UV 工具链"]
A[uv pip — 包安装]
B[uv venv — 虚拟环境]
C[uv lock — 依赖锁定]
end
Traditional --> D["三个独立工具配置分散"]
UVChain --> E["单一二进制零依赖"]
classDef old fill:#fef3c7,color:#92400e
classDef new fill:#dcfce7,color:#166534
classDef diff fill:#dbeafe,color:#1e3a8a
class P,V,PO old
class A,B,C new
class D,E diff
```
### 兼容模式 vs 原生模式
UV 支持两种使用方式:
| 模式 | 核心命令 | 适用场景 |
|------|----------|----------|
| **`uv pip` 兼容模式** | `uv pip install` / `uv pip compile` | 从 pip 迁移,完全兼容 `requirements.txt` |
| **UV 原生模式** | `uv add` / `uv lock` / `uv run` | 新项目推荐,基于 `pyproject.toml` 现代化工作流 |
> [!note] `uv pip` 与原生命令的区别
> - `uv pip install` 安装到系统或虚拟环境中的 site-packages,行为与 pip 一致
> - `uv add` 直接修改 `pyproject.toml` 中的依赖声明,并自动更新 `uv.lock`,适合有明确项目结构的项目
> - 生产环境建议优先使用原生模式,因为 lock 文件确保所有开发者使用完全一致的依赖树
### pyproject.toml 中的 UV 配置
UV 使用标准 `pyproject.toml`,通过 `[tool.uv]` 表进行专属配置:
```toml
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.110.0",
"httpx",
]
[tool.uv]
dev-dependencies = [
"pytest>=8.0",
"ruff>=0.4.0",
]
# 指定 Python 源
[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple/"
# 私有源(如需)
#[[tool.uv.sources]]
#my-private-pkg = { index = "internal" }
```
## 安装
### 操作系统安装命令
```bash
# macOS / Linux(官方推荐)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows(PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# Homebrew / apt / scoop
brew install uv # macOS
apt install uv # Ubuntu(需先添加仓库)
scoop install uv # Windows
```
> [!tip] 验证安装
> 安装完成后运行 `uv --version`,确认版本 ≥ 0.5.0。首次运行时 UV 会自动下载所需的 Python 版本并缓存,后续启动无需等待。
## 虚拟环境管理
### 创建与管理虚拟环境
```bash
# 创建虚拟环境(默认使用 .venv 目录)
uv venv
# 指定 Python 版本(自动下载缺失版本)
uv venv --python 3.12
# 指定目标路径
uv venv ./.venv-prod
```
创建成功后,激活方式与传统 venv 一致:
```bash
# macOS / Linux
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
```
### uv run — 零激活执行
```bash
# 在虚拟环境中直接运行命令,无需手动激活
uv run python main.py
# 传递参数
uv run pytest tests/ -v
# 交互式 REPL
uv run python -i
```
> [!tip] 为什么推荐使用 `uv run`?
> 省去了 `source .venv/bin/activate` 和 `deactivate` 的手动步骤。CI/CD、脚本调用、编辑器集成都可以通过 `uv run` 一次性完成——它会自行查找 `.venv` 或使用 `pyproject.toml` 中声明的环境。
## 包管理与依赖
### 原生模式:`uv add` / `uv remove`
```bash
# 添加生产依赖(写入 pyproject.toml + 更新 uv.lock)
uv add fastapi httpx
# 添加开发依赖(写入 dev-dependencies)
uv add --dev pytest ruff mypy
# 指定精确版本或范围
uv add "requests>=2.31,<3"
uv add flask==3.0.0
# 从 Git 仓库安装
uv add git+https://github.com/user/repo.git@branch
# 移除依赖
uv remove httpx
```
### `pip` 兼容模式:`uv pip`
```bash
# 安装 requirements.txt(行为与 pip install -r 一致)
uv pip install -r requirements.txt
# 生成锁文件(类似 pip-compile)
uv pip compile requirements.in -o requirements.locked.txt
# 卸载包
uv pip uninstall requests
```
### 版本解析策略
UV 使用基于 **回溯** 的高性能依赖解析器(由 pubgrub 算法驱动),与 Poetry 的行为语义相同:
```mermaid
flowchart LR
A["用户请求: fastapi>=0.110 + httpx"] --> B{"解析依赖树"}
B --> C["发现冲突: A 要求 urllib3>=2, B 要求 urllib3<2"]
C --> D["尝试替换 B 为兼容版本"]
D -->|"解决"| E["输出唯一可行解 → uv.lock"]
D -->|"无解"| F["报错: 不可解析"]
classDef ok fill:#dcfce7,color:#166534
classDef fail fill:#f99,color:#7f1d1d
class E ok
class F fail
```
## Lock 文件
### `uv.lock` 的作用
`uv.lock` 是 UV 的依赖锁文件,记录的是经过解析后 **每一个子依赖的确切版本号**(不仅是顶层依赖)。这确保了:
1. 不同机器、不同时间安装得到完全相同的依赖树
2. CI/CD 环境可复现构建结果
3. 避免 "在我机器上是好的" 问题
### 典型工作流
```bash
# 新增依赖(自动生成或更新 lock 文件)
uv add httpx
# 手动更新 lock 文件(不改变 pyproject.toml,只更新锁定版本到最新兼容版)
uv lock
# 安装 lock 文件中锁定的所有依赖
uv sync
# 同步但排除 dev 依赖(生产环境)
uv sync --no-dev
```
> [!info] uv sync vs pip install -r
> - `uv sync` 会严格对照 `uv.lock` 安装——移除多余的、更新过期的、安装缺失的
> - `pip install -r` 只看 `requirements.txt`,不会主动清理已安装但未声明的包
> - 推荐始终使用 `uv sync` 代替 `pip install -e .`
## 多项目工作区(Workspace)
UV 支持 Monorepo 风格的多项目组织:
```toml
# pyproject.toml at workspace root
[project]
name = "workspace-root"
version = "0.1.0"
requires-python = ">=3.11"
[tool.uv.workspace]
members = ["packages/*"]
optional-groups = ["docs"]
```
```toml
# packages/web/pyproject.toml
[project]
name = "web-service"
dependencies = ["shared-lib"] # 引用 workspace 内的其他项目
```
```toml
# packages/lib/pyproject.toml
[project]
name = "shared-lib"
```
```bash
# 在工作区根目录操作
uv lock # 解析整个工作区的依赖
uv sync # 安装所有成员包的依赖
uv add -p web-service fastapi # 只为特定成员添加依赖
```
## 实战示例
### 创建一个 FastAPI 项目
```bash
# ① 创建项目并初始化 venv
uv init my-api
cd my-api
uv venv --python 3.12
# ② 添加依赖
uv add fastapi uvicorn httpx
uv add --dev ruff pytest
# ③ 运行项目
uv run uvicorn main:app --reload
# ④ 运行测试
uv run pytest tests/ -v
```
### CI/CD 流水线集成
```yaml
# .github/workflows/ci.yml(简化版)
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install UV
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Sync dependencies
run: uv sync --all-extras --frozen
- name: Run tests
run: uv run pytest tests/ -v
- name: Lint
run: uv run ruff check .
```
> [!warning] `--frozen` 标志的含义
> 加上 `--frozen` 时,如果 `uv.lock` 不存在或与 `pyproject.toml` 不一致,命令会直接失败而非自动更新。这在 CI 中很重要——确保 lock 文件的变更必须经过代码审查提交。
## 常用命令速查
| 命令 | 说明 | 等价命令 |
|------|------|----------|
| `uv add <pkg>` | 添加依赖到 pyproject.toml | `uv pip install` |
| `uv remove <pkg>` | 从依赖中移除 | `uv pip uninstall` |
| `uv sync` | 按 lock 文件同步环境 | `pip install -e .` |
| `uv lock` | 生成/更新 lock 文件 | `poetry lock` |
| `uv run <cmd>` | 在环境内执行命令 | `source .venv/bin/activate && cmd` |
| `uv venv` | 创建虚拟环境 | `python -m venv .venv` |
| `uv python list` | 列出可安装的 Python 版本 | `pyenv versions` |
| `uv python install 3.12` | 下载并缓存指定 Python | `pyenv install 3.12` |
| `uv pin 3.11` | 锁定项目 Python 版本 | 设置 `.python-version` |
| `uv export` | 导出为 requirements.txt | `pip freeze > req.txt` |
## 与其他工具的对比
| 维度 | pip + venv | Poetry | uv |
|------|-----------|--------|-----|
| 语言 | Python | Python | Rust |
| 安装速度 | 慢(单线程) | 中等 | ⚡ 极快(并发) |
| 虚拟环境 | `venv` 独立命令 | `poetry env` | `uv venv` 内置 |
| Lock 文件 | 需 pip-compile | `poetry.lock` | `uv.lock` |
| pyproject.toml | 第三方支持 | ✅ 原生 | ✅ 原生 |
| 多项目/Workspace | ❌ | ✅ | ✅ |
| Python 版本管理 | ❌ 需 pyenv | ❌ 需 pyenv | ✅ `uv python install` |
| 依赖大小 | 无额外依赖 | 较重 | 单个二进制 ~15MB |
| 成熟度 | 事实标准 | 高 | 快速迭代中 |
> [!info] 迁移建议
> - **pip 用户** → 直接使用 `uv pip` 命令获得加速,零迁移成本
> - **Poetry 用户** → `uv` 的 `add/sync/lock` 工作流与 Poetry 高度相似,学习曲线平缓
> - **新项目** → 直接用 `uv` 原生命令 + `pyproject.toml` + `uv.lock`
## 关联笔记
- [[hzh/DEV/Husky]] — Git Hooks 管理(可与 uv run 结合在 pre-commit 中运行 lint)