原创

uv 使用记录:Python 环境、依赖管理与项目运行

一、uv 是什么?

uv 是 Astral 开发的 Python 包和项目管理工具,使用 Rust 编写。它可以安装 Python、创建虚拟环境、安装依赖、记录依赖版本,也可以运行 Python 程序和命令行工具。uv 官方介绍

平时开发 Python 项目,要处理的大致是这几件事:

选择 Python 版本
    ↓
创建项目的虚拟环境
    ↓
安装项目依赖
    ↓
运行代码和测试
    ↓
保存依赖配置,供其他环境安装

这些操作都可以通过 uv 完成。常用命令分别是 uv python、uv venv、uv add、uv run 和 uv sync。

已经使用 pip + requirements.txt 的项目,也可以先使用 uv pip 安装依赖,保留原来的目录结构。

二、uv 有什么用?

1. 每个项目使用自己的 Python 环境

例如,一个项目需要 Python 3.10,另一个项目使用 Python 3.11,可以分别创建虚拟环境。依赖安装在各自的 .venv 中,运行时选择对应的解释器。

2. 把依赖配置和实际安装版本记录下来

项目需要哪些包,写在 pyproject.toml 中;解析出来的具体版本及依赖关系,记录到 uv.lock。

换电脑或者交给其他人运行时,可以根据这两个文件重新安装环境,减少逐个确认版本的工作。

3. 直接在项目环境中运行命令

在项目目录执行:

uv run python main.py

uv 会准备项目环境,再使用其中的 Python 运行程序。终端里可以省去手动激活环境这一步。

4. 复用下载缓存,管理临时工具

多个项目用到相同依赖时,uv 可以复用缓存。偶尔使用的格式检查工具,还可以通过 uvx 单独运行,工具依赖保存在隔离环境中。

对我这类同时维护业务代码和实验脚本的项目,主要用到的是环境隔离、依赖记录和统一运行命令。安装耗时还会受到网络、缓存和包体积影响,这里没有做安装速度的对比测试。

三、uv 的详细使用

1. 安装 uv

Windows 可以使用官方安装脚本:

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

这条命令中的执行策略只作用于启动的 PowerShell 进程。安装完成后重新打开终端,检查命令是否可用:

uv --version

也可以通过已经安装的 Python 安装:

python -m pip install uv

两种方式选择一种即可。后面升级时,跟随原来的安装方式:

# 通过官方独立安装脚本安装
uv self update

# 通过 pip 安装,使用当时安装 uv 的那个 Python
python -m pip install --upgrade uv

Linux、macOS 的官方安装命令是:

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

不同安装方式的路径与升级说明可以查看 uv 安装文档。

2. 安装和查看 Python 版本

查看可用的 Python 版本:

uv python list

只查看已经安装的版本:

uv python list --only-installed

安装项目需要的 Python:

uv python install 3.11

这里指定 3.11,uv 会安装可用的对应补丁版本。如果项目需要固定到某个补丁版本,可以写完整版本号。

查看指定 Python 的位置:

uv python find 3.11

电脑上已经安装的 Python 也可以使用。后面创建环境时,用 --python 指定版本或解释器路径。Python 版本管理

3. 新建一个项目

下面创建一个普通应用项目:

uv init --app --no-package --python 3.11 uv-demo
cd uv-demo

--app 表示应用项目,--no-package 表示暂时按普通脚本组织代码,不配置 Python 包的构建入口。

主要文件如下:

uv-demo/
├── .python-version
├── main.py
├── pyproject.toml
└── README.md

运行一次初始化生成的程序:

uv run python main.py

执行后,项目中会生成 .venv 和 uv.lock。它们各自负责的内容如下:

文件或目录 作用
.python-version 当前项目选择的 Python 版本
pyproject.toml 项目名称、Python 版本要求、依赖声明和工具配置
uv.lock 解析后的依赖版本、来源等信息
.venv/ 本机实际运行项目的虚拟环境

.python-version 和 requires-python 需要配合使用。例如,.python-version 写 3.11,表示当前项目选择 Python 3.11;requires-python = ">=3.11" 表示项目声明支持的 Python 范围。

如果项目实际只支持 Python 3.11,可以根据实际情况将范围收窄为:

requires-python = ">=3.11,<3.12"

这项配置放在 [project] 中。修改范围后重新锁定依赖并运行测试。项目初始化方式和文件结构见 项目使用文档。

4. 添加项目依赖

以 HTTP 客户端 httpx 为例:

uv add httpx

默认情况下,这一步会更新依赖声明、锁文件,并把依赖安装到项目环境中。

也可以指定版本要求:

# 固定到指定版本
uv add "httpx==0.28.1"

# 或者设置允许使用的版本范围
uv add "httpx>=0.28.1,<0.29"

这两条是不同的版本约束写法,按项目需要选择一种。带比较符号时加引号,避免被终端当成其他语法处理。

开发阶段使用的测试和检查工具,可以单独放进开发依赖组:

uv add --dev pytest ruff

pyproject.toml 中的配置大致如下,实际版本以命令生成的内容为准:

[project]
name = "uv-demo"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "httpx>=0.28.1",
]

[dependency-groups]
dev = [
    "pytest",
    "ruff",
]

删除依赖使用:

uv remove httpx
uv remove --dev ruff

上面两条只是删除命令示例;继续下面的演示时,需要保留刚才添加的包。依赖分组和版本声明见 依赖管理文档。

5. 运行代码和测试

将 main.py 修改为:

import httpx


def get_host(url: str) -> str:
    return httpx.URL(url).host


if __name__ == "__main__":
    print(get_host("https://example.com/api"))

运行:

uv run python main.py

输出:

example.com

这段代码只解析 URL,不会发起网络请求。

新建 test_main.py:

from main import get_host


def test_get_host():
    assert get_host("https://example.com/api") == "example.com"

执行测试和代码检查:

uv run pytest -q
uv run ruff check .

如果项目按模块组织,比如前面的多智能体项目,可以在项目根目录运行:

uv run python -m multi_agent.main --demo

项目模式下,uv run 默认会先检查锁文件和环境,再执行命令。已有项目第一次使用时,要先确认根目录 pyproject.toml 中声明的依赖是否符合当前项目需要。运行命令说明

编辑器也需要选择同一个解释器。Windows 下通常是:

项目目录\.venv\Scripts\python.exe

可以在终端中打印实际解释器路径,再与编辑器配置核对:

uv run python -c "import sys; print(sys.executable)"

6. 锁定版本和同步环境

这几个命令经常放在一起用:

命令 作用
uv lock 根据项目配置生成或更新锁文件
uv lock --check 检查锁文件是否与项目配置一致
uv sync 根据项目配置和锁文件同步环境,必要时更新锁文件
uv sync --locked 要求锁文件已与配置一致,再同步环境
uv sync --frozen 直接使用已有锁文件,跳过与项目配置的一致性检查

日常修改依赖后,可以执行:

uv sync
uv run pytest -q

拉取代码后,或者在持续集成中安装依赖,可以使用:

uv sync --locked
uv run --locked pytest -q

--locked 遇到需要更新锁文件的情况会报错,可以据此发现遗漏提交的依赖变更。

部署时只安装运行依赖:

uv sync --locked --no-dev
uv run --locked --no-dev python main.py

这里没有安装开发依赖,后续运行命令也保持 --no-dev。需要开发工具时,重新执行普通的 uv sync。

另外,uv sync 默认会移除当前同步结果中多余的包。项目长期使用的依赖应通过 uv add 记录下来,便于后续环境同步。锁定与同步说明

7. 更新依赖版本

更新某一个包的锁定版本:

uv lock --upgrade-package httpx
uv sync
uv run pytest -q

更新所有依赖的锁定版本:

uv lock --upgrade
uv sync
uv run pytest -q

升级仍需满足 pyproject.toml 中的版本约束。如果配置固定了 httpx==0.28.1,需要先调整这项约束,再进行版本升级。

更新完成后查看 pyproject.toml 和 uv.lock 的变化,再提交到仓库。长期维护的项目可以按包逐个升级,出现问题时也方便定位对应变更。

8. 在旧项目中使用 uv

已有项目通常会遇到两种情况。

继续使用 requirements.txt:

在旧项目根目录创建环境,然后安装原来的依赖文件:

uv venv --python 3.11
uv pip install --python .venv/Scripts/python.exe -r requirements.txt
.\.venv\Scripts\python.exe main.py

这里显式指定解释器,可以看清依赖安装到哪个环境。

如果项目已经有可用的 .venv,直接执行安装命令即可。uv pip install 负责修改指定环境,项目的 pyproject.toml 和 uv.lock 由项目管理命令维护。

需要手动激活环境时:

.\.venv\Scripts\Activate.ps1
python main.py
deactivate

PowerShell 限制激活脚本执行时,也可以继续使用前面的完整解释器路径运行。虚拟环境使用说明

把依赖交给 uv 项目管理:

对于还没有 pyproject.toml 的旧项目,在根目录执行:

uv init --bare --python 3.11
uv python pin 3.11
uv add -r requirements.txt
uv run python main.py

已经有 pyproject.toml 的项目直接检查并补充现有配置,跳过初始化。

如果旧 requirements.txt 来自整个环境的 pip freeze,里面可能同时包含直接依赖、间接依赖和临时安装的工具。导入前先整理实际需要的包;需要保持原环境版本时,则保留相应的固定版本要求。从 pip 迁移到 uv 项目

9. 导出 requirements.txt

有些部署脚本仍然使用 requirements.txt,可以从锁文件导出:

uv export --locked --no-dev --format requirements.txt --output-file requirements.txt

这份文件来自当前锁定结果,后续依赖变更后重新导出。如果原文件仍在人工维护,可以将输出文件名改为 requirements-export.txt,先核对两份文件的差异。

普通依赖的导出文件可以交给 pip 安装。项目使用自定义包源、CUDA wheel 或本地路径依赖时,还需要把对应安装源、平台要求和文件一起交付。锁文件导出说明

继续维护 requirements 工作流时,也可以区分“直接依赖”和“完整安装列表”:

# requirements.in
httpx>=0.28.1,<0.29

先解析,再同步:

uv pip compile requirements.in --output-file requirements.txt
uv pip sync --python .venv/Scripts/python.exe requirements.txt

uv pip sync 按文件中的完整列表同步环境,会移除多余包。这里应使用解析后包含间接依赖的文件;普通手写依赖列表可以先通过 uv pip install -r 安装。requirements 锁定与同步

10. 配置镜像源和专用安装源

项目需要使用镜像源时,可以在 pyproject.toml 中添加:

[[tool.uv.index]]
name = "aliyun"
url = "https://mirrors.aliyun.com/pypi/simple/"
default = true

default = true 会将它设为默认索引,替换默认的 PyPI 地址。镜像同步进度可能影响新版本是否可用,遇到找不到版本时,可以核对官方 PyPI 与镜像中的发布情况。

只想为某次命令指定默认索引时:

uv pip install httpx --default-index https://pypi.org/simple

uv 的索引配置放在自己的配置文件或命令参数中;原有 pip 配置需要单独确认是否已迁移。索引配置说明

Python AI 项目还经常需要单独配置 PyTorch 下载源。以 CPU 版本为例,在已有配置中合并以下内容:

[[tool.uv.index]]
name = "torch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

[tool.uv.sources]
torch = { index = "torch-cpu" }

再添加依赖:

uv add torch

explicit = true 将这个索引留给显式指定的包使用,普通依赖继续使用项目的常规索引。

如果还使用 torchvision、torchaudio,需要核对它们与 torch 的版本配套关系,并按需要添加对应来源。CUDA 项目再选择适合该版本和显卡驱动的安装组合,确认索引中确实提供目标 wheel。配置方式见 uv 与 PyTorch 集成文档。

11. 运行一个带依赖的独立脚本

临时写一个格式转换、接口测试或者数据处理脚本时,可以把依赖记录在脚本本身。

先创建 hello.py:

from rich import print

print("hello uv")

给这个已有脚本声明依赖:

uv add --script hello.py rich

uv 会在文件开头写入脚本依赖信息。然后执行:

uv run hello.py

运行时会根据脚本元数据准备隔离环境。分享这个文件时,依赖说明也保留在文件里。

只临时使用一次依赖,也可以写在运行命令中:

uv run --with rich hello.py

长期复用的脚本可以使用前面的元数据方式保存依赖;需要固定解析结果时,还可以使用 uv lock --script hello.py 生成对应锁文件。独立脚本使用说明

12. 使用 uvx 运行工具

偶尔检查一份代码,可以直接执行:

uvx ruff check .

uvx 是 uv tool run 的简写,工具在隔离环境中运行。需要固定工具版本时,可以使用 工具名@版本号 的形式。

经常使用的工具可以安装为持久工具:

uv tool install ruff
uv tool list

安装后如果终端找不到命令,检查工具可执行目录是否已加入 PATH,必要时执行 uv tool update-shell 后重新打开终端。

项目本身依赖 Ruff 做代码检查时,可以继续使用 uv add --dev ruff 和 uv run ruff check .,这样团队运行的是项目锁定的工具版本。工具运行与安装

13. 查看环境和处理常见问题

查看当前安装的包和依赖关系:

uv pip list --python .venv/Scripts/python.exe
uv pip check --python .venv/Scripts/python.exe
uv tree

uv pip check 检查当前已安装包声明的依赖是否满足;uv tree 查看项目依赖关系。环境检查命令

运行时提示 ModuleNotFoundError:

先确认运行脚本用的是哪个 Python:

uv run python -c "import sys; print(sys.executable)"

再确认项目是否声明了缺失的依赖。项目模式下用 uv add 包名 补充;requirements 模式下检查目标 .venv 的安装结果。

虚拟环境提示 No module named pip:

uv 创建的环境通常不会额外安装 pip。可以直接使用 uv 安装依赖:

uv pip install --python .venv/Scripts/python.exe -r requirements.txt

我的多智能体项目环境中也遇到了这种情况,使用这条命令即可继续管理依赖。

解析依赖时提示 Python 版本不满足:

一起检查 .python-version、pyproject.toml 中的 requires-python,以及依赖包支持的 Python 范围。uv 会按项目声明的兼容范围解析依赖;只修改本机解释器时,项目配置仍可能声明着其他版本。

命令安装成功,但编辑器仍然报找不到包:

将编辑器的项目解释器切换到 .venv/Scripts/python.exe,再重新运行。终端和编辑器使用同一个环境后,才能读取到同一组依赖。

14. 提交哪些文件,以及日常命令速查

普通应用项目提交代码时,一起提交:

pyproject.toml
uv.lock
.python-version

.venv/ 放进 .gitignore。其他人拿到代码后,在兼容的 Python 和系统环境中执行 uv sync --locked,重新安装项目依赖。

日常可以按下面这张表查命令:

要做的事 命令
安装 Python 3.11 uv python install 3.11
新建普通应用项目 uv init --app --no-package --python 3.11 项目名
记录项目 Python 版本 uv python pin 3.11
添加运行依赖 uv add 包名
添加开发依赖 uv add --dev 包名
运行程序 uv run python main.py
运行测试 uv run pytest
按已有锁文件安装 uv sync --locked
更新某个包的锁定版本 uv lock --upgrade-package 包名
安装旧项目依赖 uv pip install -r requirements.txt
执行独立脚本 uv run 脚本.py
临时使用命令行工具 uvx 工具名
正文到此结束