概念概述
https://docs.astral.sh/uv/concepts/
阅读概念文档以了解更多关于 uv 的功能:
寻找快速了解功能的介绍?请查看Guides部分。
2024年11月19日
Projects
https://docs.astral.sh/uv/concepts/projects/
项目帮助管理跨越多个文件的 Python 代码。
提示:想了解如何使用 uv 创建项目的介绍?请先查看项目指南。
在项目中工作是uv体验的核心部分。了解更多关于使用项目的信息:
2024年11月19日
项目结构和文件
https://docs.astral.sh/uv/concepts/projects/layout/
The pyproject.toml
Python项目元数据定义在一份pyproject.toml
文件中。uv需要这个文件来识别项目的根目录。
提示:可以使用uv init
来创建一个新的项目。有关详细信息,请参阅创建项目。
一个最小化的项目定义包括名称和版本:
pyproject.toml
[project]
name = "example"
version = "0.1.0"
附加的项目元数据和配置包括:
项目环境
当使用 uv 在项目上工作时,uv 会根据需要创建一个虚拟环境。虽然一些 uv 命令会创建一个临时环境(例如,uv run --isolated
),但 uv 还会管理一个持久的环境,其中包含项目及其依赖项,存放在 pyproject.toml
旁边的 .venv
目录中。它存储在项目内部,以便编辑器可以轻松找到——它们需要这个环境来提供代码补全和类型提示。不建议将 .venv
目录包含在版本控制中;它将通过内部的 .gitignore
文件自动排除。
要在项目环境中运行命令,请使用 uv run
。或者,可以将项目环境激活为普通的虚拟环境。
当调用 uv run
时,如果项目环境尚不存在,它将创建该环境;如果已存在,将确保它是最新的。也可以使用 uv sync
显式创建项目环境。有关详细信息,请参阅锁定和同步文档。
不推荐手动修改项目环境,例如使用 uv pip install
。对于项目依赖项,请使用 uv add
将包添加到环境中。对于一次性需求,请使用 uvx
或 uv run --with
。
提示:如果您不希望 uv 管理项目环境,请将 managed = false
设置为禁用项目的自动锁定和同步。例如:
pyproject.toml
[tool.uv]
managed = false
lockfile
uv 在 pyproject.toml
旁边创建一个 uv.lock
文件。
uv.lock
是一个 通用 或 跨平台 锁文件,它捕获了所有可能的 Python 标记(如操作系统、架构和 Python 版本)下将要安装的包。
与用于指定项目广泛需求的 pyproject.toml
不同,lockfile 包含了项目环境中实际安装的确切版本。此文件应提交到版本控制中,以便在机器之间实现一致且可重复的安装。
A 锁定文件确保在项目上工作的开发者使用一致的一组包版本。此外,它确保在将项目作为应用程序部署时,知道使用的确切包版本集。
锁文件在使用项目环境的 uv 调用期间 自动创建和更新,例如 uv sync
和 uv run
。锁文件也可以使用 uv lock
显式更新。
uv.lock
是一个可读的 TOML 文件,但由 uv 管理,不应手动编辑。目前没有 Python 标准的锁文件格式,因此此文件的格式特定于 uv,不能被其他工具使用。
创建项目
https://docs.astral.sh/uv/concepts/projects/init/
uv 支持使用 uv init
创建项目。
在创建项目时,uv 支持两种基本模板:应用程序 和 库。默认情况下,uv 将为应用程序创建项目。可以使用 --lib
标志来创建库项目。
目标目录
uv将在工作目录中创建一个项目,或者通过提供名称在目标目录中创建,例如uv init foo
。如果目标目录中已经存在一个项目,即存在pyproject.toml
文件,uv将错误退出。
应用程序
应用程序项目适用于Web服务器、脚本和命令行界面。
应用程序是 uv init
的默认目标,也可以使用 --app
标志来指定。
uv init example-app
该项目包括一个 pyproject.toml
,一个示例文件(main.py
),一个自述文件,以及一个 Python 版本固定文件(.python-version
)。
$ tree example-app
example-app
├── .python-version
├── README.md
├── main.py
└── pyproject.toml
注意:在 v0.6.0 之前,uv 创建了一个名为 hello.py
的文件,而不是 main.py
。
pyproject.toml
包含基本元数据。它不包含构建系统,它不是一个 包 并且不会被安装到环境中:
pyproject.toml
[project]
name = "example-app"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []
样本文件定义了一个 main
函数和一些标准样板代码:
main.py
def main():
print("Hello from example-app!")
if __name__ == "__main__":
main()
Python 文件可以使用 uv run
执行:
$ cd example-app
$ uv run main.py
Hello from example-project!
打包的应用程序
许多用例需要打包。例如,如果你正在创建一个将发布到 PyPI 的命令行界面,或者你想要在专用目录中定义测试。
可以使用 --package
标志来创建打包的应用程序:
uv init --package example-pkg
源代码被移动到一个包含模块目录和 __init__.py
文件的 src
目录中:
$ tree example-pkg
example-pkg
├── .python-version
├── README.md
├── pyproject.toml
└── src
└── example_pkg
└── __init__.py
一个 build system 被定义,因此项目将被安装到环境中:
pyproject.toml
[project]
name = "example-pkg"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []
[project.scripts]
example-pkg = "example_pkg:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Tip:--build-backend
选项可以用来请求一个替代的构建系统。
command 定义已包含:
pyproject.toml
[project]
name = "example-pkg"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []
[project.scripts]
example-pkg = "example_pkg:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
命令可以通过 uv run
执行:
$ cd example-pkg
$ uv run example-pkg
Hello from example-pkg!
库
库为其他项目提供函数和对象以供使用。库旨在构建和分发,例如,通过将它们上传到 PyPI。
可以通过使用 --lib
标志来创建库:
uv init --lib example-lib
注意:使用 --lib
等同于使用 --package
。库始终需要一个打包的项目。
与 打包应用程序 一样,使用了一个 src
布局。包含了一个 py.typed
标记,以指示消费者可以从库中读取类型:
$ tree example-lib
example-lib
├── .python-version
├── README.md
├── pyproject.toml
└── src
└── example_lib
├── py.typed
└── __init__.py
注意:在开发库时,src
布局尤其有价值。它确保库与项目根目录中的任何 python
调用隔离,并且分布式库代码与项目其他源代码很好地分离。
一个 构建系统 被定义,因此项目将被安装到环境中:
pyproject.toml
[project]
name = "example-lib"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Tip : 您可以通过使用 --build-backend
与 hatchling
、flit-core
、pdm-backend
、setuptools
、maturin
或 scikit-build-core
来选择不同的构建后端模板。如果您想要创建一个 带有扩展模块的库,则需要一个替代后端。
创建的模块定义了一个简单的API函数:
init.py
def hello() -> str:
return "Hello from example-lib!"
您可以使用 uv run
导入并执行它:
$ cd example-lib
$ uv run python -c "import example_lib; print(example_lib.hello())"
Hello from example-lib!
带有扩展模块的项目
大多数Python项目是“纯Python”,这意味着它们不定义其他语言(如C、C++、FORTRAN或Rust)中的模块。然而,具有扩展模块的项目通常用于对性能敏感的代码。
创建一个具有扩展模块的项目需要选择一个替代的构建系统。uv支持使用以下支持构建扩展模块的构建系统创建项目:
- maturin 用于具有Rust的项目
- scikit-build-core 用于具有C、C++、FORTRAN、Cython的项目
使用 --build-backend
标志指定构建系统:
uv init --build-backend maturin example-ext
注意:使用 --build-backend
意味着使用 --package
。
项目包含一个 Cargo.toml
和一个 lib.rs
文件,以及典型的 Python 项目文件:
$ tree example-ext
example-ext
├── .python-version
├── Cargo.toml
├── README.md
├── pyproject.toml
└── src
├── lib.rs
└── example_ext
├── __init__.py
└── _core.pyi
注意:如果使用 scikit-build-core
,您将看到 CMake 配置和一个 main.cpp
文件。
Rust 库定义了一个简单的函数:
src/lib.rs
use pyo3::prelude::*;
#[pyfunction]
fn hello_from_bin() -> String {
"Hello from example-ext!".to_string()
}
#[pymodule]
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(hello_from_bin, m)?)?;
Ok(())
}
Python 模块导入它:
src/example_ext/init.py
from example_ext._core import hello_from_bin
def main() -> None:
print(hello_from_bin())
命令可以通过 uv run
执行:
$ cd example-ext
$ uv run example-ext
Hello from example-ext!
Important:
更改 lib.rs
或 main.cpp
中的扩展代码将需要运行 --reinstall
以重新构建它们。
创建一个最小项目
如果您只想创建一个 pyproject.toml
,请使用 --bare
选项:
uv init example --bare
uv 将跳过创建 Python 版本标记文件、README 文件以及任何源目录或文件。此外,uv 不会初始化版本控制系统(即,git
)。
$ tree example-bare
example-bare
└── pyproject.toml
uv 也不会向 pyproject.toml
添加额外的元数据,例如 description
或 authors
。
[project]
name = "example"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []
--bare
选项可以与 --lib
或 --build-backend
等其他选项一起使用 —— 在这种情况下,uv 仍然会配置构建系统,但不会创建预期的文件结构。
当使用 --bare
时,仍可以按需启用附加功能:
uv init example --bare --description "Hello world" --author-from git --vcs git --python-pin
管理依赖关系
依赖关系表
项目的依赖关系定义在几个表中:
- project.dependencies:已发布的依赖项。
- project.optional-dependencies:已发布的可选依赖项,或称为“额外”依赖项。
- dependency-groups:开发用的本地依赖项。
- tool.uv.sources:开发期间依赖项的替代源。
注意:即使项目不打算发布,也可以使用 project.dependencies
和 project.optional-dependencies
表格。dependency-groups
是一个最近标准化的特性,可能还没有被所有工具支持。
uv支持使用 uv add
和 uv remove
修改项目的依赖项,但也可以通过直接编辑 pyproject.toml
来更新依赖项元数据。
添加依赖
要添加一个依赖项:
uv add httpx
一条记录将被添加到 project.dependencies
表中:
pyproject.toml
[project] name = "example" version = "0.1.0" dependencies = ["httpx>=0.27.2"]
-dev
、--group
或 --optional
标志可用于将依赖关系添加到替代表中。
依赖关系将包括一个约束,例如,对于包的最新兼容版本,>=0.27.2
。可以提供另一种约束:
uv add "httpx>=0.20"
当从一个非包注册表的其他源添加依赖项时,uv 将在源表中添加一个条目。例如,当从 GitHub 添加 httpx
时:
uv add "httpx @ git+https://github.com/encode/httpx"
pyproject.toml
将包含一个 Git 源条目:
pyproject.toml
[project]
name = "example"
version = "0.1.0"
dependencies = [
"httpx",
]
[tool.uv.sources]
httpx = { git = "https://github.com/encode/httpx" }
如果无法使用依赖项,uv 将显示错误。
uv add "httpx>9999"
导入依赖
在 requirements.txt
文件中声明的依赖可以通过 -r
选项添加到项目中:
uv add -r requirements.txt
移除依赖
要移除一个依赖:
uv remove httpx
--dev
, --group
或 --optional
标志可以用来从特定表中删除一个依赖。
如果为移除的依赖项定义了 源,并且没有其他对该依赖项的引用,它也将被移除。
更改依赖
要更改现有依赖项,例如,为 httpx
使用不同的约束:
uv add "httpx>0.1.0"
Note : 在这个示例中,我们正在更改 pyproject.toml
中依赖项的约束。依赖项的锁定版本只有在必要时才会更新以满足新的约束。要强制包版本更新到约束范围内的最新版本,请使用 --upgrade-package <name>
,例如:
uv add "httpx>0.1.0" --upgrade-package httpx
查看lockfile文档以获取更多关于升级软件包的详细信息。
请求不同的依赖源将更新 tool.uv.sources
表,例如,在开发期间从本地路径使用 httpx
:
uv add "httpx @ ../httpx"
平台特定依赖
为确保依赖项仅在特定平台或特定Python版本上安装,请使用环境标记。
例如,要在Linux上安装jax
,但不在Windows或macOS上安装:
uv add "jax; sys_platform == 'linux'"
生成的 pyproject.toml
文件将包括依赖定义中的环境标记:
pyproject.toml
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["jax; sys_platform == 'linux'"]
同样,要在 Python 3.11 及更高版本中包含 numpy
:
uv add "numpy; python_version >= '3.11'"
查看 Python 的 环境标记 文档,以获取可用标记和运算符的完整枚举。
提示:依赖源也可以按平台更改。
项目依赖
project.dependencies
表代表在上传到 PyPI 或构建 wheel 时使用的依赖项。单个依赖项使用 依赖指定符 语法指定,表格遵循 PEP 621 标准。
project.dependencies
定义了项目所需的包列表,以及安装它们时应使用的版本约束。每个条目包括一个依赖项名称和版本。条目可能包括为特定平台包的额外功能或环境标记。例如:
pyproject.toml
[project]
name = "albatross"
version = "0.1.0"
dependencies = [
# Any version in this range
"tqdm >=4.66.2,<5",
# Exactly this version of torch
"torch ==2.2.2",
# Install transformers with the torch extra
"transformers[torch] >=4.39.3,<5",
# Only install this package on older python versions
# See "Environment Markers" for more information
"importlib_metadata >=7.1.0,<8; python_version < '3.10'",
"mollymawk ==0.1.0"
]
依赖源
tool.uv.sources
表扩展了标准依赖表,提供了备选依赖源,这些依赖源在开发过程中使用。
依赖源增加了对 project.dependencies
标准不支持的一些常见模式的支持,例如可编辑安装和相对路径。例如,要从项目根目录相对目录中安装 foo
:
pyproject.toml
[project]
name = "example"
version = "0.1.0"
dependencies = ["foo"]
[tool.uv.sources]
foo = { path = "./packages/foo" }
以下依赖源由 uv 支持:
重要:uv 仅尊重源。如果使用其他工具,则仅使用标准项目表中的定义。如果使用其他工具进行开发,则需要在其他工具的格式中重新指定源表中提供的任何元数据。
索引
要从特定索引添加 Python 包,请使用 --index
选项:
uv add torch --index pytorch=https://download.pytorch.org/whl/cpu
uv 将在 [[tool.uv.index]]
中存储索引,并添加一个 [tool.uv.sources]
条目:
pyproject.toml
[project]
dependencies = ["torch"]
[tool.uv.sources]
torch = { index = "pytorch" }
[[tool.uv.index]]
name = "pytorch"
url = "https://download.pytorch.org/whl/cpu"
提示:上述示例仅在 x86-64 Linux 上有效,因为 PyTorch 索引的特定原因。有关设置 PyTorch 的更多信息,请参阅PyTorch 指南。
使用一个 index
源将包固定到指定的索引——它将不会从其他索引中下载。
当定义索引时,可以包含一个 explicit
标志来指示该索引 仅 应用于在 tool.uv.sources
中明确指定它的包。如果没有设置 explicit
,则如果在其他地方找不到,则可能从索引中解析其他包。
pyproject.toml
[[tool.uv.index]]
name = "pytorch"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
Git
要添加 Git 依赖源,请在 Git 兼容的 URL(即您会用于 git clone
的 URL)前缀为 git+
。
例如:
uv add git+https://github.com/encode/httpx
pyproject.toml
[project]
dependencies = ["httpx"]
[tool.uv.sources]
httpx = { git = "https://github.com/encode/httpx" }
特定 Git 引用可以请求,例如,一个标签:
uv add git+https://github.com/encode/httpx --tag 0.27.0
pyproject.toml
[project] dependencies = ["httpx"] [tool.uv.sources] httpx = { git = "https://github.com/encode/httpx", tag = "0.27.0" }
或者,一个分支:
uv add git+https://github.com/encode/httpx --branch main
pyproject.toml
[project]
dependencies = ["httpx"]
[tool.uv.sources]
httpx = { git = "https://github.com/encode/httpx", tag = "0.27.0" }
或者,一个修订(提交):
uv add git+https://github.com/encode/httpx --rev 326b9431c761e1ef1e00b9f760d1f654c8db48c6
pyproject.toml
[project]
dependencies = ["httpx"]
[tool.uv.sources]
httpx = { git = "https://github.com/encode/httpx", branch = "main" }
A 子目录
可以指定,如果包不在存储库根目录中:
uv add git+https://github.com/langchain-ai/langchain#subdirectory=libs/langchain
pyproject.toml
[project]
dependencies = ["httpx"]
[tool.uv.sources]
httpx = { git = "https://github.com/encode/httpx", rev = "326b9431c761e1ef1e00b9f760d1f654c8db48c6" }
URL
要添加 URL 源,提供一个 https://
URL,指向一个 wheel 文件(以 .whl
结尾)或源代码分发(通常以 .tar.gz
或 .zip
结尾;有关所有支持的格式,请参阅此处)。
例如:
uv add "https://files.pythonhosted.org/packages/5c/2d/3da5bdf4408b8b2800061c339f240c1802f2e82d55e50bd39c5a881f47f0/httpx-0.27.0.tar.gz"
将生成一个 pyproject.toml
,其中包含:
pyproject.toml
[project]
dependencies = ["langchain"]
[tool.uv.sources]
langchain = { git = "https://github.com/langchain-ai/langchain", subdirectory = "libs/langchain" }
URL 依赖也可以通过 pyproject.toml
使用 { url = <url> }
语法手动添加或编辑。如果源分发不在存档根目录中,可以指定一个 子目录
。
路径
要添加路径源,请提供轮文件的路径(以 .whl
结尾),源分发版(通常以 .tar.gz
或 .zip
结尾;有关所有支持的格式,请参阅此处),或包含 pyproject.toml
的目录。
例如:
uv add /example/foo-0.1.0-py3-none-any.whl
将生成一个 pyproject.toml
,内容如下:
pyproject.toml
[project]
dependencies = ["httpx"]
[tool.uv.sources]
httpx = { url = "https://files.pythonhosted.org/packages/5c/2d/3da5bdf4408b8b2800061c339f240c1802f2e82d55e50bd39c5a881f47f0/httpx-0.27.0.tar.gz" }
路径也可以是相对路径:
uv add ./foo-0.1.0-py3-none-any.whl
或者,项目目录的路径:
uv add ~/projects/bar/
Important:
An 可编辑的安装 默认情况下不用于路径依赖。可以为项目目录请求可编辑的安装:
uv add --editable ~/projects/bar/
对于同一存储库中的多个包,工作区可能更合适。
工作区成员
要声明对工作区成员的依赖,请添加成员名称并使用 { workspace = true }
。所有工作区成员都必须明确声明。工作区成员始终是可编辑的。有关工作区的更多详细信息,请参阅工作区文档。
pyproject.toml
[project]
dependencies = ["foo==0.1.0"]
[tool.uv.sources]
foo = { workspace = true }
[tool.uv.workspace]
members = [
"packages/foo"
]
平台特定的来源
您可以通过提供与 dependency specifiers 兼容的环境标记来限制来源仅适用于特定的平台或 Python 版本。
例如,要从 GitHub 拉取 httpx
,但仅限于 macOS,请使用以下命令:
pyproject.toml
[project]
dependencies = ["httpx"]
[tool.uv.sources]
httpx = { git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" }
通过在源上指定标记,uv 仍将在所有平台上包含 httpx
,但在 macOS 上将从 GitHub 下载源,在其他所有平台上回退到 PyPI。
多个来源
您可以通过提供一个由 PEP 508 兼容的环境标记区分的来源列表,为单个依赖指定多个来源。
例如,为了在 macOS 和 Linux 上拉取不同的 httpx
标签:
pyproject.toml
[project]
dependencies = ["httpx"]
[tool.uv.sources]
httpx = [
{ git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" },
{ git = "https://github.com/encode/httpx", tag = "0.24.1", marker = "sys_platform == 'linux'" },
]
此策略扩展到根据环境标记使用不同的索引。例如,根据平台从不同的 PyTorch 索引安装 torch
:
pyproject.toml
[project]
dependencies = ["torch"]
[tool.uv.sources]
torch = [
{ index = "torch-cpu", marker = "platform_system == 'Darwin'"},
{ index = "torch-gpu", marker = "platform_system == 'Linux'"},
]
[[tool.uv.index]]
name = "torch-cpu"
url = "https://download.pytorch.org/whl/cpu"
[[tool.uv.index]]
name = "torch-gpu"
url = "https://download.pytorch.org/whl/cu124"
禁用源
要指示uv忽略tool.uv.sources
表(例如,模拟使用包已发布的元数据进行解析),请使用--no-sources
标志:
uv lock --no-sources
--no-sources
的使用还将阻止 uv 发现任何可以满足给定依赖项的 工作区成员。
可选依赖
将项目发布为库时,为了减少默认依赖树,让一些功能可选是很常见的。例如,Pandas 有一个用于处理 Excel 文件的 excel
额外模块 和一个用于可视化的 plot
额外模块,以避免安装 Excel 解析器和 matplotlib
,除非有人明确要求。可以使用 package[<extra>]
语法请求额外模块,例如,pandas[plot, excel]
。
可选依赖在 [project.optional-dependencies]
中指定,这是一个 TOML 表格,它将额外模块名称映射到其依赖项,遵循 依赖指定符 语法。
可选依赖可以在 tool.uv.sources
中与正常依赖项具有相同的条目。
pyproject.toml
[project]
name = "pandas"
version = "1.0.0"
[project.optional-dependencies]
plot = [
"matplotlib>=3.6.3"
]
excel = [
"odfpy>=1.4.1",
"openpyxl>=3.1.0",
"python-calamine>=0.1.7",
"pyxlsb>=1.0.10",
"xlrd>=2.0.1",
"xlsxwriter>=3.0.5"
]
要将一个可选依赖项添加,请使用 --optional <额外>
选项:
uv add httpx --optional network
Note
如果您有相互冲突的可选依赖项,除非您明确地将它们声明为冲突,否则解析将失败。
源也可以声明仅适用于特定的可选依赖项。例如,根据可选的 cpu
或 gpu
附加组件从不同的 PyTorch 指数中提取 torch
:
pyproject.toml
[project]
dependencies = []
[project.optional-dependencies]
cpu = [
"torch",
]
gpu = [
"torch",
]
[tool.uv.sources]
torch = [
{ index = "torch-cpu", extra = "cpu" },
{ index = "torch-gpu", extra = "gpu" },
]
[[tool.uv.index]]
name = "torch-cpu"
url = "https://download.pytorch.org/whl/cpu"
[[tool.uv.index]]
name = "torch-gpu"
url = "https://download.pytorch.org/whl/cu124"
开发依赖
与可选依赖不同,开发依赖是本地-only 的,并且在发布到 PyPI 或其他索引时 不会 包含在项目需求中。因此,开发依赖不包括在 [项目]
表中。
开发依赖可以在 tool.uv.sources
中有条目,就像正常依赖一样。
要添加开发依赖,请使用 --dev
标志:
uv add --dev pytest
uv 使用 [dependency-groups]
表(如 PEP 735 中定义的)来声明开发依赖项。上面的命令将创建一个 dev
组:
pyproject.toml
[dependency-groups] dev = [ "pytest >=8.1.1,<9" ]
dev
组有特殊处理;有 --dev
、--only-dev
和 --no-dev
标志来切换其依赖项的包含或排除。查看 --no-default-groups
以禁用所有默认组。此外,dev
组默认 同步。
依赖分组
开发依赖可以被划分为多个组,使用 --group
标志。
例如,要在 lint
组中添加一个开发依赖:
uv add --group lint ruff
结果为以下 [dependency-groups]
定义:
pyproject.toml
[dependency-groups]
dev = [
"pytest"
]
lint = [
"ruff"
]
一旦定义了组,可以使用 --all-groups
、--no-default-groups
、--group
、--only-group
和 --no-group
选项来包含或排除它们的依赖项。
提示:--dev
、--only-dev
和 --no-dev
标志分别等同于 --group dev
、--only-group dev
和 --no-group dev
。
uv 要求所有依赖组之间相互兼容,并在创建锁文件时一起解析所有组。
如果在一个组中声明的依赖项与另一个组中的依赖项不兼容,uv 将无法通过错误解析项目的需求。
注意:如果您有相互冲突的依赖组,除非您明确声明它们为冲突的,否则解析将失败。
默认组
默认情况下,uv 将 dev
依赖组包含在环境中(例如,在 uv run
或 uv sync
过程中)。可以通过使用 tool.uv.default-groups
设置来更改要包含的默认组。
pyproject.toml
[tool.uv]
default-groups = ["dev", "foo"]
提示:要在 uv run
或 uv sync
中禁用此行为,请使用 --no-default-groups
。要排除特定的默认组,请使用 --no-group <name>
。
旧版 dev-dependencies
在 [dependency-groups]
标准化之前,uv 使用 tool.uv.dev-dependencies
字段来指定开发依赖项,例如:
pyproject.toml
[tool.uv]
dev-dependencies = [
"pytest"
]
依赖项声明在本节中,将与 dependency-groups.dev
中的内容合并。最终,dev-dependencies
字段将被弃用并移除。
注意:如果存在 tool.uv.dev-dependencies
字段,则 uv add --dev
将使用现有部分而不是添加新的 dependency-groups.dev
部分。
构建依赖
如果一个项目以 Python 包 的形式构建,它可能声明了构建项目所需的依赖项,但这些依赖项不是运行项目所需的。这些依赖项在 build-system.requires
下的 [build-system]
表中指定,遵循 PEP 518。
例如,如果一个项目使用 setuptools
作为其构建后端,它应该将 setuptools
声明为构建依赖项:
pyproject.toml
[project]
name = "pandas"
version = "0.1.0"
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"
默认情况下,uv 将在解析构建依赖项时尊重 tool.uv.sources
。例如,要使用本地版本的 setuptools
进行构建,请将源添加到 tool.uv.sources
:
pyproject.toml
[project]
name = "pandas"
version = "0.1.0"
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"
[tool.uv.sources]
setuptools = { path = "./packages/setuptools" }
在发布一个包时,我们建议运行 uv build --no-sources
以确保在 tool.uv.sources
被禁用时,包能够正确构建,就像在使用其他构建工具(如 pypa/build)时一样。
可编辑依赖
一个目录中 Python 包的常规安装首先构建一个 wheel,然后将其安装到您的虚拟环境中,并复制所有源文件。当包的源文件被编辑时,虚拟环境将包含过时的版本。
可编辑安装通过在虚拟环境中添加到项目的链接(一个 .pth
文件)来解决此问题,该链接指示解释器直接包含源文件。
可编辑安装有一些限制(主要是:构建后端需要支持它们,并且原生模块在导入之前不会重新编译),但它们对开发很有用,因为虚拟环境将始终使用包的最新更改。
uv 默认使用可编辑安装来安装工作区包。
要添加一个可编辑依赖,使用 --editable
标志:
uv add --editable ./path/foo
或者,要在一个工作区中禁用可编辑依赖项:
uv add --no-editable ./path/foo
在项目中运行命令
当在一个项目上工作时,它会被安装到虚拟环境 .venv
中。默认情况下,此环境与当前shell是隔离的,因此需要项目才能调用的调用,例如 python -c "import example"
,将会失败。相反,请使用 uv run
在项目环境中运行命令:
uv run python -c "import example"
当使用 run
时,uv 将确保在运行给定命令之前,项目环境是最新的。
给定的命令可以由项目环境提供,或者存在于项目环境之外,例如:
$ # Presuming the project provides `example-cli`
$ uv run example-cli foo
$ # Running a `bash` script that requires the project to be available
$ uv run bash scripts/foo.sh
请求额外的依赖
每次调用都可以请求额外的依赖或不同版本的依赖。
使用 --with
选项来包含调用中的一个依赖,例如,请求 httpx
的不同版本:
uv run --with httpx==0.26.0 python -c "import httpx; print(httpx.__version__)"
0.26.0
$ uv run --with httpx==0.25.0 python -c "import httpx; print(httpx.__version__)"
0.25.0
请求的版本将被尊重,无论项目需求如何。例如,即使项目需要 httpx==0.24.0
,上面的输出也会相同。
运行脚本
声明内联元数据的脚本将在与项目隔离的环境中自动执行。有关详细信息,请参阅脚本指南。
例如,给定一个脚本:
example.py
# /// script
# dependencies = [
# "httpx",
# ]
# ///
import httpx
resp = httpx.get("https://peps.python.org/api/peps.json")
data = resp.json()
print([(k, v["title"]) for k, v in data.items()][:10])
uv 运行 example.py
命令会以 独立 的方式运行,仅包含列出的依赖项。
旧版 Windows 脚本
支持 旧版 setuptools 脚本。这些类型的脚本是由 setuptools 在 .venv\Scripts
中安装的附加文件。
目前仅支持以 .ps1
、.cmd
和 .bat
扩展名结尾的旧版脚本。
例如,下面是一个运行命令提示符脚本的示例。
uv run --with nuitka==2.6.7 -- nuitka.cmd --version
此外,您不需要指定扩展名。uv
将会自动为您查找以 .ps1
、.cmd
和 .bat
结尾的文件,并按照这个顺序执行。
uv run --with nuitka==2.6.7 -- nuitka --version
锁定和同步
锁定是将您项目的依赖项解析为锁定文件的过程。同步是将锁定文件中的部分包安装到项目环境的过程。
自动锁定和同步
在 uv 中,锁定和同步是 自动 的。例如,当使用 uv run
时,在调用请求的命令之前,项目会被锁定和同步。这确保了项目环境始终是最新的。同样,例如 uv tree
这样的读取锁定文件的命令,在运行之前也会自动更新它。
要禁用自动锁定,请使用 --locked
选项:
uv run --locked ...
如果锁文件不是最新的,uv 将会引发错误而不是更新锁文件。
要使用锁文件而不检查其是否最新,请使用 --frozen
选项:
uv run --frozen ...
同样,要运行命令而不检查环境是否最新,请使用 --no-sync
选项:
uv run --no-sync ...
检查锁文件是否更新
在考虑锁文件是否更新时,uv 会检查它是否与项目元数据匹配。例如,如果您在 pyproject.toml
中添加了一个依赖项,则锁文件将被认为是过时的。同样,如果您更改了依赖项的版本约束,以至于锁定的版本被排除在外,则锁文件将被认为是过时的。然而,如果您更改版本约束,使得现有的锁定版本仍然包含在内,则锁文件仍将被认为是更新的。
您可以通过向 uv lock
命令传递 --check
标志来检查锁文件是否更新:
uv lock --check
这与其他命令的 --locked
标志等效。
Important: uv 不会在包的新版本发布时考虑锁定文件过时 —— 如果您想升级依赖项,需要显式更新锁定文件。有关详细信息,请参阅升级锁定包版本的文档。
创建锁文件
虽然锁文件是自动创建的 自动创建,但也可以使用 uv lock
显式创建或更新锁文件:
uv lock
同步环境
当环境正在同步时自动,也可以使用 uv sync
显式同步:
uv sync
手动同步环境尤其有用,以确保您的编辑器具有正确的依赖项版本。
可编辑安装
当环境同步时,uv 将项目(以及其他工作区成员)作为 可编辑 包安装,这样更改不需要重新同步即可反映在环境中。
要退出此行为,请使用 --no-editable
选项。
注意:如果项目没有定义构建系统,则无法安装。有关详细信息,请参阅构建系统文档。
保留多余包
默认情况下,同步是“精确”的,这意味着它将删除锁文件中不存在的任何包。
要保留多余包,请使用 --inexact
选项:
uv sync --inexact
同步可选依赖
uv 从 [project.optional-dependencies]
表中读取可选依赖。这些通常被称为 “额外依赖”。
uv 默认不会同步额外依赖。使用 --extra
选项来包含一个额外依赖。
uv sync --extra foo
要快速启用所有附加功能,请使用 --all-extras
选项。
查看可选依赖项文档以获取如何管理可选依赖项的详细信息。
同步开发依赖
uv 从 [dependency-groups]
表中读取开发依赖(如 PEP 735 中定义的)。
dev
组被特别处理,并且默认同步。有关更改默认设置的详细信息,请参阅 默认组 文档。
可以使用 --no-dev
标志来排除 dev
组。
可以使用 --only-dev
标志来安装 dev
组 而不包含 项目及其依赖项。
可以使用 --all-groups
、--no-default-groups
、--group <name>
、--only-group <name>
和 --no-group <name>
选项包括或排除其他组。--only-group
的语义与 --only-dev
相同,项目将不会包括在内。然而,--only-group
还会排除默认组。
组排除始终优先于包含,因此对于以下命令:
uv sync --no-group foo --group foo
foo
组将不会被安装。
查看开发依赖项文档以获取如何管理开发依赖项的详细信息。
升级锁定包版本
存在 uv.lock
文件时,uv 将在运行 uv sync
和 uv lock
时优先选择之前锁定的包版本。只有当项目的依赖约束排除了之前的锁定版本时,包版本才会更改。
要升级所有包:
uv lock --upgrade
要将单个包升级到最新版本,同时保留所有其他包的锁定版本:
uv lock --upgrade-package <package>
将单个软件包升级到特定版本:
uv lock --upgrade-package <package>==<version>
在所有情况下,升级都受限于项目的依赖约束。例如,如果项目为某个包定义了上限版本,则升级不会超出该版本。
注意:uv 对 Git 依赖项应用类似的逻辑。例如,如果 Git 依赖项引用了 main
分支,uv 将优先选择现有 uv.lock
文件中锁定的提交 SHA,而不是 main
分支上的最新提交,除非使用了 --upgrade
或 --upgrade-package
标志。
这些标志也可以提供给 uv sync
或 uv run
以更新锁文件和环境。
导出锁文件
如果您需要将 uv 与其他工具或工作流程集成,您可以使用 uv export --format requirements-txt
将 uv.lock
导出为 requirements.txt
格式。生成的 requirements.txt
文件可以通过 uv pip install
安装,或者使用其他工具如 pip
。
通常,我们不推荐同时使用 uv.lock
和 requirements.txt
文件。如果您发现自己正在导出 uv.lock
文件,请考虑提交一个问题来讨论您的用例。
配置项目
Python 版本要求
项目可以在 pyproject.toml
文件的 project.requires-python
字段中声明项目支持的 Python 版本。
建议设置 requires-python
值:
pyproject.toml
[project]
name = "example"
version = "0.1.0"
requires-python = ">=3.12"
Python 版本要求决定了项目中允许使用的 Python 语法,并影响依赖版本的选择(它们必须支持相同的 Python 版本范围)。
入口点
入口点是已安装包用于宣传接口的官方术语。这些包括:
Important:
使用入口点表需要定义一个 构建系统。
命令行界面
项目可以为项目定义命令行界面(CLIs),并在 pyproject.toml
的 [project.scripts]
表中指定。
例如,要声明一个名为 hello
的命令,该命令调用 example
模块中的 hello
函数:
pyproject.toml
[project.scripts]
hello = "example:hello"
然后,可以从控制台运行该命令:
uv run hello
图形用户界面
项目可以在 pyproject.toml
中的 [project.gui-scripts]
表中定义项目的图形用户界面(GUI)。
Important:
这些在其他平台上表现相同,只是在 Windows 上,它们通过一个 GUI 可执行文件被包装,以便可以在没有控制台的情况下启动。命令行界面。
例如,要声明一个名为 hello
的命令,该命令在 example
模块中调用 app
函数:
pyproject.toml
[project.gui-scripts]
hello = "example:app"
插件入口点
项目可以在 pyproject.toml
的 [project.entry-points\]
表中定义插件发现入口。
例如,要将 example-plugin-a
包注册为 example
的插件:
pyproject.toml
[project.entry-points.'example.plugins']
a = "example_plugin_a"
然后,在 example
中,插件将通过以下方式加载:
example/init.py
from importlib.metadata import entry_points
for plugin in entry_points(group='example.plugins'):
plugin.load()
注意:group
键可以是一个任意值,它不需要包含包名或 “plugins”。然而,建议通过包名来命名空间键,以避免与其他包发生冲突。
构建系统
构建系统决定了项目应该如何打包和安装。项目可以在 pyproject.toml
文件的 [build-system]
表中声明和配置构建系统。
uv 使用构建系统的存在来决定项目中是否包含应该在项目虚拟环境中安装的包。如果没有定义构建系统,uv 将不会尝试构建或安装项目本身,只会安装其依赖项。如果定义了构建系统,uv 将构建并安装项目到项目环境中。
可以提供 --build-backend
选项给 uv init
来创建一个具有适当布局的打包项目。可以提供 --package
选项给 uv init
来创建一个使用默认构建系统的打包项目。
注意:虽然uv没有构建系统定义将不会构建和安装当前项目,但其他包中不需要存在 [build-system]
表格。出于历史原因,如果没有定义构建系统,则使用 setuptools.build_meta:__legacy__
来构建包。你依赖的包可能没有明确声明它们的构建系统,但仍然可以安装。同样,如果你添加了对本地包的依赖或使用 uv pip
安装它,uv将始终尝试构建和安装它。
构建系统选项
构建系统用于启用以下功能:
- 从发行版中包含或排除文件
- 可编辑的安装行为
- 动态项目元数据
- 原生代码的编译
- 共享库的 vendoring
要配置这些功能,请参阅您选择的构建系统的文档。
项目打包
如构建系统中所述,Python项目必须构建后才能安装。这个过程通常被称为“打包”。
你可能需要打包,如果你想:
- 向项目中添加命令
- 将项目分发给他人
- 使用
src
和test
布局 - 编写库
你可能不需要打包,如果你:
- 编写脚本
- 构建一个简单的应用程序
- 使用平面布局
虽然uv通常使用构建系统的声明来确定项目是否应该打包,但uv也允许通过tool.uv.package
设置来覆盖此行为。
设置tool.uv.package = true
将强制项目构建并安装到项目环境中。如果没有定义构建系统,uv将使用setuptools传统后端。
设置tool.uv.package = false
将强制项目不构建并安装到项目环境中。uv在与项目交互时将忽略声明的构建系统。
项目环境路径
可以使用 UV_PROJECT_ENVIRONMENT
环境变量来配置项目虚拟环境路径(默认为 .venv
)。
如果提供了一个相对路径,它将相对于工作区根目录进行解析。如果提供了一个绝对路径,它将直接使用,即不会为环境创建子目录。如果提供的路径不存在环境,uv 将创建它。
此选项可用于写入系统 Python 环境,尽管不推荐这样做。uv sync
默认会从环境中移除多余的包,因此可能会使系统处于损坏状态。
要针对系统环境,将 UV_PROJECT_ENVIRONMENT
设置为 Python 安装的前缀。例如,在基于 Debian 的系统上,这通常是 /usr/local
:
$ python -c "import sysconfig; print(sysconfig.get_config_var('prefix'))"
/usr/local
要针对此环境,您会导出 UV_PROJECT_ENVIRONMENT=/usr/local
。
Important: 如果提供了绝对路径并且此设置在多个项目中使用,环境将被每个项目中的调用覆盖。此设置仅推荐用于CI或Docker镜像中的单个项目。
注意:默认情况下,uv 在项目操作期间不会读取 VIRTUAL_ENV
环境变量。如果 VIRTUAL_ENV
设置为与项目环境不同的路径,将显示警告。可以使用 --active
标志来选择尊重 VIRTUAL_ENV
。可以使用 --no-active
标志来静默警告。
受限分辨率环境
如果你的项目支持更有限的平台或 Python 版本,你可以通过 environments
设置限制要解决的平台的集合,该设置接受一个 PEP 508 环境标记列表。例如,为了将锁文件限制在 macOS 和 Linux 上,并排除 Windows:
pyproject.toml
[tool.uv]
environments = [
"sys_platform == 'darwin'",
"sys_platform == 'linux'",
]
查看更多请参阅分辨率文档。
必需环境
如果你的项目 必须 支持特定的平台或 Python 版本,你可以通过 required-environments
设置标记该平台为必需。例如,要指定项目支持 Intel macOS:
pyproject.toml
[tool.uv]
required-environments = [
"sys_platform == 'darwin' and platform_machine == 'x86_64'",
]
required-environments
设置仅适用于不发布源分布的包(如 PyTorch),因为此类包只能在由该包发布的预构建二进制分布(wheels)覆盖的环境上安装。
查看更多信息的分辨率文档。
构建隔离
默认情况下,uv 在隔离的虚拟环境中构建所有包,遵循 PEP 517。一些包与构建隔离不兼容,无论是故意(例如,由于使用了重的构建依赖项,最常见的是 PyTorch)还是无意(例如,由于使用了遗留的打包设置)。
要为特定依赖项禁用构建隔离,将其添加到 pyproject.toml
中的 no-build-isolation-package
列表:
pyproject.toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet"]
[tool.uv]
no-build-isolation-package = ["cchardet"]
安装不使用构建隔离的包需要先在项目环境中安装包的构建依赖,然后再安装包本身。这可以通过将构建依赖和需要它们的包分离成不同的额外部分来实现。例如:
pyproject.toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[project.optional-dependencies]
build = ["setuptools", "cython"]
compile = ["cchardet"]
[tool.uv]
no-build-isolation-package = ["cchardet"]
考虑到以上内容,用户首先需要同步 build
依赖项:
uv sync --extra build
+ cython==3.0.11
+ foo==0.1.0 (from file:///Users/crmarsh/workspace/uv/foo)
+ setuptools==73.0.1
随后是 编译
依赖项:
uv sync --extra compile
+ cchardet==2.1.7
- cython==3.0.11
- setuptools==73.0.1
请注意,uv sync --extra compile
默认会卸载 cython
和 setuptools
软件包。要保留构建依赖项,请在第二次 uv sync
调用中包含这两个 extras:
uv sync --extra build
$ uv sync --extra build --extra compile
一些包,如上面的 cchardet
,仅需要在 uv sync
的 安装 阶段构建依赖。其他,如 flash-attn
,即使在 解析 阶段只需解决项目的锁定文件,也需要它们的构建依赖存在。
在这种情况下,必须在运行任何 uv lock
或 uv sync
命令之前安装构建依赖项,使用较低级别的 uv pip
API。例如,给定:
pyproject.toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["flash-attn"]
[tool.uv]
no-build-isolation-package = ["flash-attn"]
您可以使用以下命令序列来同步 flash-attn
:
uv venv
$ uv pip install torch setuptools
$ uv sync
或者,您可以通过在 dependency-metadata
设置中提前提供 flash-attn
元数据来跳过在依赖项解析阶段构建包的需要。例如,要提前提供 flash-attn
元数据,请在您的 pyproject.toml
中包含以下内容:
pyproject.toml
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
Tip:要确定类似 flash-attn
这样的包的元数据,请导航到相应的 Git 仓库,或在 PyPI 上查找并下载该包的源代码分布。包的要求通常可以在 setup.py
或 setup.cfg
文件中找到。
(如果软件包包含构建后的发行版,您可以将它解压以找到 METADATA
文件;然而,构建后的发行版的存在会否定在前期提供元数据的需求,因为它已经对 uv 可用。)
一旦包含,您还可以再次使用两步 uv sync
流程来安装构建依赖项。给定以下 pyproject.toml
:
pyproject.toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[project.optional-dependencies]
build = ["torch", "setuptools", "packaging"]
compile = ["flash-attn"]
[tool.uv]
no-build-isolation-package = ["flash-attn"]
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
您可以使用以下命令序列来同步 flash-attn
:
uv sync --extra build
$ uv sync --extra build --extra compile
注意:在 tool.uv.dependency-metadata
中的 version
字段对于基于注册表的依赖项是可选的(当省略时,uv 将假设元数据适用于所有版本的包),但对于直接 URL 依赖项(如 Git 依赖项)则是必需的。
可编辑模式
默认情况下,项目将以可编辑模式安装,这样对源代码的更改将立即反映在环境中。
uv sync
和 uv run
都接受一个 --no-editable
标志,该标志指示 uv 以非可编辑模式安装项目。
--no-editable
旨在用于部署用例,例如构建 Docker 容器,在这种情况下,项目应包含在部署环境中,而不依赖于原始源代码。
冲突依赖
uv 要求项目声明的所有可选依赖(“extras”)之间相互兼容,并在创建锁文件时一起解决所有可选依赖。
如果在一个额外依赖中声明的可选依赖与另一个额外依赖中的不兼容,uv 将无法解决项目的需求,并抛出错误。
为了解决这个问题,uv 支持声明冲突的额外依赖。例如,考虑两个相互冲突的可选依赖集:
pyproject.toml
[project.optional-dependencies]
extra1 = ["numpy==2.1.2"]
extra2 = ["numpy==2.0.0"]
如果您使用上述依赖项运行 uv lock
,解析将失败:
uv lock
x No solution found when resolving dependencies:
`-> Because myproject[extra2] depends on numpy==2.0.0 and myproject[extra1] depends on numpy==2.1.2, we can conclude that myproject[extra1] and
myproject[extra2] are incompatible.
And because your project requires myproject[extra1] and myproject[extra2], we can conclude that your projects's requirements are unsatisfiable.
但是如果您指定 extra1
和 extra2
存在冲突,uv 将分别解决它们。在 tool.uv
部分指定冲突:
pyproject.toml
[tool.uv]
conflicts = [
[
{ extra = "extra1" },
{ extra = "extra2" },
],
]
现在,运行 uv lock
将会成功。注意,现在您不能同时安装 extra1
和 extra2
。
$ uv sync --extra extra1 --extra extra2
Resolved 3 packages in 14ms
error: extra `extra1`, extra `extra2` are incompatible with the declared conflicts: {`myproject[extra1]`, `myproject[extra2]`}
此错误发生是因为同时安装 extra1
和 extra2
将会导致在同一个环境中安装同一软件包的两个不同版本。
以上处理冲突额外参数的策略也适用于依赖分组:
pyproject.toml
[dependency-groups]
group1 = ["numpy==2.1.2"]
group2 = ["numpy==2.0.0"]
[tool.uv]
conflicts = [
[
{ group = "group1" },
{ group = "group2" },
],
]
与冲突的扩展的唯一区别是,您需要使用 group
而不是 extra
。
构建发行版
要将您的项目分发给他人(例如,上传到像 PyPI 这样的索引),您需要将其构建成可分发格式。
Python 项目通常以源发行版(sdists)和二进制发行版(wheels)的形式进行分发。前者通常是一个包含项目源代码和一些附加元数据的 .tar.gz
或 .zip
文件,而后者是一个包含预构建工件可以直接安装的 .whl
文件。
Important: 当使用 uv build
时,uv 充当 构建前端 的角色,并且只确定要使用的 Python 版本并调用构建后端。构建的详细信息,例如包含的文件和分发文件名,由定义在 [build-system\]
中的构建后端确定。有关构建配置的信息可以在相应工具的文档中找到。
使用 uv build
uv build
可以用来构建您的项目的源分布和二进制分布。默认情况下,uv build
将在当前目录中构建项目,并将构建的工件放置在 dist/
子目录中:
$ uv build
$ ls dist/
example-0.1.0-py3-none-any.whl
example-0.1.0.tar.gz
您可以通过提供 uv build
的路径来在不同的目录中构建项目,例如:uv build path/to/project
。
uv build
首先将构建一个源分发版,然后从这个源分发版构建一个二进制分发版(wheel)。
您可以将 uv build
限制为仅构建源分布,使用 uv build --sdist
,二进制分布,使用 uv build --wheel
,或者从源构建两种分布,使用 uv build --sdist --wheel
。
构建约束
uv build
接受 --build-constraint
参数,该参数可用于在构建过程中约束任何构建需求版本。当与 --require-hashes
结合使用时,uv 将强制要求用于构建项目的需求与特定的已知哈希值匹配,以确保可重复性。
例如,给定以下 constraints.txt
文件:
setuptools==68.2.2 --hash=sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a
运行以下命令将使用指定的 setuptools
版本构建项目,并验证下载的 setuptools
发行版与指定的哈希值匹配:
uv build --build-constraint constraints.txt --require-hashes
使用工作区
受 Cargo 中同名概念的启发,工作区是“一个或多个称为 工作区成员 的包的集合,这些包被一起管理。”
工作区通过将它们拆分为具有共同依赖的多个包来组织大型代码库。想象一下:一个基于 FastAPI 的 Web 应用程序,以及一系列作为单独的 Python 包进行版本控制和维护的库,所有这些都位于同一个 Git 仓库中。
在工作区中,每个包都定义了自己的 pyproject.toml
文件,但工作区共享一个单一的 lockfile,确保工作区使用一致的依赖集进行操作。
因此,uv lock
一次操作整个工作区,而 uv run
和 uv sync
默认操作工作区根目录,尽管两者都接受 --package
参数,允许您从任何工作区目录运行特定工作区成员的命令。
入门
要创建一个工作区,请在 pyproject.toml
中添加一个 tool.uv.workspace
表,这将隐式地在该包的根目录创建一个工作区。
提示:
默认情况下,在现有包内部运行 uv init
将会将新创建的成员添加到工作区中,如果工作区根目录中尚不存在,则会创建一个 tool.uv.workspace
表。
在定义工作区时,您必须指定 members
(必需)和 exclude
(可选)键,这些键分别指导工作区包含或排除特定的目录作为成员,并接受glob列表:
pyproject.toml
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { workspace = true }
[tool.uv.workspace]
members = ["packages/*"]
exclude = ["packages/seeds"]
每个由 members
glob 包含的目录(且不是由 exclude
glob 排除的)都必须包含一个 pyproject.toml
文件。然而,工作空间成员可以是 任一 应用 或 库;两者在工作空间上下文中都受到支持。
每个工作空间都需要一个根,它也是一个工作空间成员。在上面的例子中,albatross
是工作空间的根,工作空间成员包括 packages
目录下的所有项目,但 seeds
除外。
默认情况下,uv run
和 uv sync
在工作区根目录上运行。例如,在上面的例子中,uv run
和 uv run --package albatross
是等效的,而 uv run --package bird-feeder
将在 bird-feeder
包中运行命令。
工作空间源
在一个工作空间中,通过tool.uv.sources
促进了工作空间成员之间的依赖关系,如下所示:
pyproject.toml
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { workspace = true }
[tool.uv.workspace]
members = ["packages/*"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
在这个示例中,albatross
项目依赖于 bird-feeder
项目,后者是工作空间的一个成员。tool.uv.sources
表中的 workspace = true
键值对表示 bird-feeder
依赖项应由工作空间提供,而不是从 PyPI 或其他注册表中获取。
注意:工作区成员之间的依赖关系是可编辑的。
任何在工作区根目录中的 tool.uv.sources
定义都适用于所有成员,除非在特定成员的 tool.uv.sources
中被覆盖。例如,给定以下 pyproject.toml
:
pyproject.toml
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { workspace = true }
tqdm = { git = "https://github.com/tqdm/tqdm" }
[tool.uv.workspace]
members = ["packages/*"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
每个工作空间成员默认会从GitHub安装tqdm
,除非特定成员在其自己的tool.uv.sources
表中覆盖了tqdm
条目。
工作空间布局
最常见的工作空间布局可以想象为一个根项目以及一系列伴随的库。
例如,继续以上示例,这个工作空间在 albatross
有一个明确的根,packages
目录下有两个库(bird-feeder
和 seeds
):
albatross
├── packages
│ ├── bird-feeder
│ │ ├── pyproject.toml
│ │ └── src
│ │ └── bird_feeder
│ │ ├── __init__.py
│ │ └── foo.py
│ └── seeds
│ ├── pyproject.toml
│ └── src
│ └── seeds
│ ├── __init__.py
│ └── bar.py
├── pyproject.toml
├── README.md
├── uv.lock
└── src
└── albatross
└── main.py
由于在 pyproject.toml
中排除了 seeds
,该工作空间共有两个成员:albatross
(根)和 bird-feeder
。
当使用(或不使用)工作区
工作区旨在简化在单个存储库内开发多个相互关联的包。随着代码库复杂性的增长,将其拆分为更小的、可组合的包可能会有所帮助,每个包都有自己的依赖和版本约束。
工作区有助于强制执行隔离和关注点的分离。例如,在uv中,我们为核心库和命令行界面有单独的包,这使得我们能够独立于CLI测试核心库,反之亦然。
工作区的一些其他常见用例包括:
- 一个库,其中性能关键子例程实现在一个扩展模块中(Rust、C++等)。
- 一个具有插件系统的库,其中每个插件都是一个单独的工作区包,依赖于根。
工作区不适用于成员之间存在冲突要求或希望每个成员都有一个单独的虚拟环境的情况。在这种情况下,路径依赖通常更可取。例如,而不是将albatross
及其成员分组在工作区中,您始终可以定义每个包为其独立的工程,将包间依赖定义为tool.uv.sources
中的路径依赖:
pyproject.toml
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { path = "packages/bird-feeder" }
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
这种方法传达了许多相同的优势,但允许对依赖项解析和虚拟环境管理进行更精细的控制(缺点是 uv run --package
现在不再可用;相反,必须从相关的包目录中运行命令)。
最后,uv的工作空间强制执行整个工作空间的单个requires-python
,取所有成员requires-python
值的交集。如果您需要支持在不受工作空间其余部分支持的Python版本上测试某个成员,您可能需要使用uv pip
在单独的虚拟环境中安装该成员。
注意:由于 Python 不提供依赖隔离,uv 无法确保一个包只使用其声明的依赖而不会使用其他内容。对于工作空间特定来说,uv 无法确保包不会导入由另一个工作空间成员声明的依赖。
工具
工具是提供命令行界面的 Python 包。
注意:查看工具指南以了解如何使用工具界面——本文件讨论了工具管理的详细信息。
uv 工具
接口
uv 包含一个用于与工具交互的专用接口。可以使用 uv tool run
来调用工具,而不需要安装。在这种情况下,它们的依赖项会安装在一个临时的虚拟环境中,该环境与当前项目隔离。
由于运行工具而不安装它们是非常常见的,因此提供了一个 uvx
别名用于 uv tool run
—— 这两个命令完全等价。为了简洁,文档将主要使用 uvx
而不是 uv tool run
。
工具也可以使用 uv tool install
进行安装,在这种情况下,它们的可执行文件将在 PATH 上可用——仍然使用一个隔离的虚拟环境,但在命令完成后不会删除。
执行与安装
在大多数情况下,使用 uvx
执行工具比安装工具更合适。如果您需要工具对系统上的其他程序可用,例如,如果您不控制的某个脚本需要该工具,或者您在一个 Docker 镜像中,并希望将工具提供给用户,那么安装工具是有用的。
工具环境
当使用 uvx
运行工具时,虚拟环境存储在 uv 缓存目录中,并被视为可丢弃的,即如果您运行 uv cache clean
,环境将被删除。环境只缓存以减少重复调用的开销。如果环境被删除,将自动创建一个新的环境。
当使用 uv tool install
安装工具时,虚拟环境将在 uv 工具目录中创建。除非工具被卸载,否则环境不会删除。如果手动删除环境,则工具将无法运行。
工具版本
除非请求特定版本,否则 uv tool install
将安装请求工具的最新版本。uvx
将在首次调用时使用请求工具的最新可用版本 *。之后,除非请求不同版本、清理缓存或刷新缓存,否则 uvx
将使用缓存的工具版本。
例如,要运行 Ruff 的特定版本:
uvx ruff@0.6.0 --version
ruff 0.6.0
后续调用 uvx
将使用最新的版本,而不是缓存的版本。
uvx ruff --version
ruff 0.6.2
但是,如果发布了Ruff的新版本,除非刷新缓存,否则不会使用。
要请求 Ruff 的最新版本并刷新缓存,请使用 @latest
后缀:
uvx ruff@latest --version
0.6.2
一旦使用 uv tool install
安装了工具,uvx
将默认使用已安装的版本。
例如,在安装较旧版本的 Ruff 之后:
uv tool install ruff==0.5.0
ruff
和 uvx ruff
的版本相同:
$ ruff --version
ruff 0.5.0
$ uvx ruff --version
ruff 0.5.0
然而,您可以通过明确请求最新版本来忽略已安装的版本,例如:
uvx ruff@latest --version
0.6.2
或者,通过使用 --isolated
标志,将避免刷新缓存但忽略已安装版本:
uvx --isolated ruff --version
0.6.2
uv 工具安装
也会尊重 {package}@{version}
和 {package}@latest
指定符,例如:
uv tool install ruff@latest
$ uv tool install ruff@0.6.0
工具目录
默认情况下,uv 工具目录命名为 tools
,位于 uv 应用程序状态目录中,例如,~/.local/share/uv/tools
。位置可以通过 UV_TOOL_DIR
环境变量进行自定义。
要显示工具安装目录的路径:
uv tool dir
工具环境放置在与工具包同名的目录中,例如,.../tools/<name>
。
Important:
工具环境 不 旨在被直接修改。强烈建议永远不要手动修改工具环境,例如使用 pip
操作。
升级工具
工具环境可以通过 uv tool upgrade
进行升级,或者通过后续的 uv tool install
操作完全重新创建。
要升级工具环境中的所有包
uv tool upgrade black
要将单个包在工具环境中升级:
uv tool upgrade black --upgrade-package click
工具升级将尊重安装工具时提供的版本约束。例如,uv tool install black >=23,<24
后跟 uv tool upgrade black
将将 Black 升级到范围 >=23,<24
内的最新版本。
要替换版本约束,请使用 uv tool install
重新安装工具:
uv tool install black>=24
同样,工具升级将保留在安装工具时提供的设置。例如,uv tool install black --prerelease allow
然后跟 uv tool upgrade black
将保留 --prerelease allow
设置。
注意:工具升级将重新安装工具的可执行文件,即使它们没有改变。
要在升级过程中重新安装软件包,请使用 --reinstall
和 --reinstall-package
选项。
要在工具环境中重新安装所有包
uv tool upgrade black --reinstall
要在工具环境中重新安装单个包:
uv tool upgrade black --reinstall-package click
包含额外的依赖
在工具执行期间可以包含额外的包:
uvx --with <extra-package> <tool>
并且,在工具安装期间:
uv tool install --with <extra-package> <tool-package>
--with
选项可以多次提供以包含额外的包。
The --with
选项支持包规范,因此可以请求特定版本:
uvx --with <extra-package>==<version> <tool-package>
如果请求的版本与工具包的要求冲突,包解析将失败,并且命令将出错。
工具可执行文件
工具可执行文件包括由 Python 包提供的所有控制台入口点、脚本入口点和二进制脚本。在 Unix 上,工具可执行文件被链接到 bin
目录,在 Windows 上则被复制。
bin
目录
可执行文件按照 XDG 标准安装到用户 bin
目录中,例如,~/.local/bin
。与 uv 中的其他目录方案不同,XDG 标准在 所有平台 上使用,特别是包括 Windows 和 macOS —— 在这些平台上没有明确的替代位置来放置可执行文件。安装目录由第一个可用的环境变量确定:
$UV_TOOL_BIN_DIR
$XDG_BIN_HOME
$XDG_DATA_HOME/../bin
$HOME/.local/bin
工具包依赖提供的可执行文件不会安装。
路径 (PATH
)
bin
目录必须包含在 PATH
变量中,以便工具的可执行文件可以从 shell 中访问。如果它不在 PATH
中,将会显示一个警告。可以使用 uv tool update-shell
命令将 bin
目录添加到常见 shell 配置文件中的 PATH
。
覆盖可执行文件
安装工具不会覆盖 bin
目录中先前未由 uv 安装的可执行文件。例如,如果使用 pipx
安装了一个工具,则 uv tool install
将会失败。可以使用 --force
标志来覆盖此行为。
与 uv run
的关系
调用 uv tool run <name>
(或 uvx <name>
)几乎等同于:
uv run --no-project --with <name> -- <name>
然而,在使用uv的工具接口时,有一些值得注意的差异:
--with
选项不是必需的——所需的软件包将从命令名称中推断出来。- 临时环境缓存在一个专用位置。
--no-project
标志不需要——工具总是独立于项目运行。- 如果一个工具已经安装,
uv tool run
将使用已安装的版本,但uv run
不会。
如果工具不应该与项目隔离,例如在运行pytest
或mypy
时,则应使用uv run
而不是uv tool run
。
Python 版本
一个 Python 版本由一个 Python 解释器(即 python
可执行文件)、标准库和其他支持文件组成。
管理和系统 Python 安装
由于系统通常已经存在一个 Python 安装,uv 支持发现 Python 版本。然而,uv 也支持 安装 Python 版本 本身。为了区分这两种类型的 Python 安装,uv 将其安装的 Python 版本称为 管理型 Python 安装,而将所有其他 Python 安装称为 系统型 Python 安装。
注意:uv无法区分操作系统安装的Python版本和由其他工具安装和管理的Python版本。例如,如果使用pyenv
管理Python安装,在uv中仍将其视为系统 Python版本。
请求特定版本
在大多数 uv 命令中,可以使用 --python
标志请求特定的 Python 版本。例如,在创建虚拟环境时:
uv venv --python 3.11.6
uv 将确保 Python 3.11.6 可用——如果需要,则会下载并安装它——然后使用它创建虚拟环境。
以下 Python 版本请求格式均受支持:
<version>
(e.g.,3
,3.12
,3.12.3
)<version-specifier>
(e.g.,>=3.12,<3.13
)<implementation>
(e.g.,cpython
orcp
)<implementation>@<version>
(e.g.,cpython@3.12
)<implementation><version>
(e.g.,cpython3.12
orcp312
)<implementation><version-specifier>
(e.g.,cpython>=3.12,<3.13
)<implementation>-<version>-<os>-<arch>-<libc>
(e.g.,cpython-3.12.3-macos-aarch64-none
)
此外,可以使用以下命令请求特定的系统 Python 解释器:
<executable-path>
(e.g.,/opt/homebrew/bin/python3
)<executable-name>
(e.g.,mypython3
)<install-dir>
(e.g.,/some/environment/
)
默认情况下,uv会在系统上找不到Python版本时自动下载。
此行为可以通过python-downloads
选项禁用。
Python 版本文件
.python-version
文件可用于创建默认的 Python 版本请求。uv 会在工作目录及其父目录中搜索 .python-version
文件。如果未找到,uv 将检查用户级配置目录。可以使用上述描述的任何请求格式,尽管推荐使用版本号以提高与其他工具的互操作性。
可以使用 uv python pin
命令在当前目录中创建 .python-version
文件。
可以使用 uv python pin --global
命令在用户配置目录中创建全局 .python-version
文件。
可以使用 --no-config
禁用 .python-version
文件的发现。
uv 不会在项目或工作空间边界之外搜索 .python-version
文件(用户配置目录除外)。
安装 Python 版本
uv 包含了一系列可供下载的 macOS、Linux 和 Windows 的 CPython 和 PyPy 分发版。
提示:默认情况下,Python 版本会在需要时自动下载,而不使用 uv python install
。
要安装特定版本的 Python:
uv python install 3.12.3
要安装最新补丁版本:
uv python install 3.12
要安装满足约束条件的版本:
uv python install '>=3.8,<3.10'
要安装多个版本:
uv python install 3.9 3.10 3.11
要安装特定实现:
uv python install pypy
所有 Python 版本请求 格式都受支持,除非是用于请求本地解释器的那种,例如文件路径。
默认情况下,uv python install
将会验证是否已安装受管理的 Python 版本,或者安装最新版本。
如果存在 .python-version
文件,uv 将会安装文件中列出的 Python 版本。
需要多个 Python 版本的项目可以定义一个 .python-versions
文件。
如果存在,uv 将会安装文件中列出的所有 Python 版本。
Important: Python 可用的版本在每个 uv 发布版中都是固定的。要安装新的 Python 版本,你可能需要升级 uv。
安装 Python 可执行文件
Important: 支持安装 Python 可执行文件目前处于 预览 状态,这意味着该行为是实验性的,并且可能会发生变化。
要将 Python 可执行文件安装到您的 PATH
中,请提供 --preview
选项:
uv python install 3.12 --preview
这将安装一个请求版本的 Python 可执行文件到 ~/.local/bin
中,例如,作为 python3.12
。
Tip
如果 ~/.local/bin
不在您的 PATH
中,您可以使用 uv tool update-shell
来添加它。
要安装 python
和 python3
可执行文件,请包含 --default
选项:
uv python install 3.12 --default --preview
在安装 Python 可执行文件时,uv 只有在可执行文件由 uv 管理的情况下才会覆盖现有的可执行文件——例如,如果 ~/.local/bin/python3.12
已存在,uv 不会在没有 --force
标志的情况下覆盖它。
uv 将更新它管理的可执行文件。然而,它默认会优先选择每个 Python 小版本的最新补丁版本。例如:
uv python install 3.12.7 --preview # Adds `python3.12` to `~/.local/bin`
$ uv python install 3.12.6 --preview # Does not update `python3.12`
$ uv python install 3.12.8 --preview # Updates `python3.12` to point to 3.12.8
Python 项目版本
uv 将在项目命令调用期间尊重 pyproject.toml
文件中定义的 requires-python
的 Python 要求。除非有其他请求,例如通过 .python-version
文件或 --python
标志,否则将使用与要求兼容的第一个 Python 版本。
查看可用的 Python 版本
要列出已安装和可用的 Python 版本:
uv python list
默认情况下,其他平台和旧补丁版本的下载被隐藏。
要查看所有版本:
uv python list --all-versions
要查看其他平台的 Python 版本:
uv python list --all-platforms
要排除下载并仅显示已安装的 Python 版本:
uv python list --only-installed
查看uv python list参考以获取更多详细信息。
查找 Python 可执行文件
要查找 Python 可执行文件,请使用 uv python find
命令:
uv python find
默认情况下,这将显示第一个可用的 Python 可执行文件的路径。有关可执行文件如何发现的详细信息,请参阅发现规则。
此接口也支持许多 请求格式,例如,要查找具有 3.11 或更高版本的 Python 可执行文件:
uv python find '>=3.11'
默认情况下,uv python find
将包括虚拟环境中的 Python 版本。如果在工作目录或任何父目录中找到 .venv
目录,或者设置了 VIRTUAL_ENV
环境变量,它将优先于 PATH
中任何 Python 可执行文件。
要忽略虚拟环境,请使用 --system
标志:
uv python find --system
Python 版本的发现
在搜索 Python 版本时,以下位置会被检查:
UV_PYTHON_INSTALL_DIR
中的管理 Python 安装。- 作为
python
、python3
或python3.x
(在 macOS 和 Linux 上)或python.exe
(在 Windows 上)的PATH
上的 Python 解释器。 - 在 Windows 上,Windows 注册表中的 Python 解释器和 Microsoft Store 中的 Python 解释器(参见
py --list-paths
),这些解释器与请求的版本匹配。
在某些情况下,uv 允许使用来自虚拟环境的 Python 版本。在这种情况下,虚拟环境中的解释器会在上述搜索安装之前进行检查,以确保其与请求的兼容性。有关详细信息,请参阅 pip 兼容的虚拟环境发现 文档。
在执行发现时,将忽略非可执行文件。每个发现的可执行文件都会查询元数据以确保它满足 请求的 Python 版本。如果查询失败,则跳过该可执行文件。如果可执行文件满足请求,则使用它而无需检查其他可执行文件。
在搜索管理 Python 版本时,uv 将优先考虑较新版本。在搜索系统 Python 版本时,uv 将使用第一个兼容版本——而不是最新版本。
如果系统上找不到 Python 版本,uv 将检查兼容的管理 Python 版本下载。
Python 预发布版本
Python 预发布版本默认不会被选中。如果请求中没有其他可用的安装版本匹配,将使用 Python 预发布版本。例如,如果只有预发布版本可用,则将使用该版本,但否则将使用稳定发布版本。同样,如果提供了预发布 Python 可执行文件的路径,则没有其他 Python 版本与请求匹配,将使用预发布版本。
如果可用的预发布 Python 版本与请求匹配,uv 将不会下载稳定版本的 Python,而是使用预发布版本。
禁用自动 Python 下载
默认情况下,uv 将在需要时自动下载 Python 版本。
可以使用 python-downloads
选项 来禁用此行为。默认情况下,它设置为 automatic
;设置为 manual
以仅允许在 uv python install
期间下载 Python。
提示:
python-downloads
设置可以在 持久性配置文件 中设置,以更改默认行为,或者可以将 --no-python-downloads
标志传递给任何 uv 命令。
需求或禁用托管 Python 版本
默认情况下,uv 将尝试使用系统上找到的 Python 版本,并且仅在必要时才下载托管 Python 版本。要忽略系统 Python 版本,并仅使用托管 Python 版本,请使用 --managed-python
标志:
uv python list --managed-python
同样,要忽略管理版Python版本,仅使用系统Python版本,请使用 --no-managed-python
标志:
uv python list --no-managed-python
要更改uv在配置文件中的默认行为,请使用python-preference
设置。
调整 Python 版本偏好
python-preference
设置确定是否优先使用系统上已存在的 Python 安装,还是使用 uv 下载和安装的 Python。
默认情况下,python-preference
被设置为 managed
,这表示优先使用管理的 Python 安装,而不是系统 Python 安装。然而,系统 Python 安装仍然比下载管理的 Python 版本更受青睐。
以下可用的替代选项包括:
only-managed
:仅使用管理的 Python 安装;从不使用系统 Python 安装。等同于--managed-python
。system
:优先使用系统 Python 安装,而不是管理的 Python 安装。only-system
:仅使用系统 Python 安装;从不使用管理的 Python 安装。等同于--no-managed-python
。
注意:可以在不更改首选项的情况下禁用自动 Python 版本下载 禁用。
Python 实现支持
uv 支持 CPython、PyPy 和 GraalPy Python 实现。如果某个 Python 实现不受支持,uv 将无法发现其解释器。
可以使用长名或短名请求实现:
- CPython:
cpython
,cp
- PyPy:
pypy
,pp
- GraalPy:
graalpy
,gp
实现名称请求不区分大小写。
有关支持的格式的更多详细信息,请参阅Python 版本请求文档。
管理Python发行版
uv支持下载和安装CPython和PyPy发行版。
CPython 发行版
由于 Python 不发布官方可分发的 CPython 二进制文件,uv 因此使用 Astral 项目中预先构建的发行版,即 python-build-standalone
python-build-standalone。python-build-standalone
也被许多其他 Python 项目使用,例如 Rye,Mise,以及 bazelbuild/rules_python。
uv 的 Python 发行版是自包含的,高度可移植且性能出色。虽然 Python 可以从源代码构建,就像 pyenv
这样的工具一样,但这样做需要预先安装的系统依赖项,并且创建优化、性能出色的构建(例如,启用 PGO 和 LTO)非常缓慢。
这些发行版存在一些行为上的奇怪之处,通常是由于可移植性造成的;并且,目前 uv 不支持在基于 musl 的 Linux 发行版(如 Alpine Linux)上安装它们。有关详细信息,请参阅 python-build-standalone 的奇怪之处 文档。
分辨率
https://docs.astral.sh/uv/concepts/resolution/
分辨率是将一系列需求转换为满足这些需求的包版本列表的过程。分辨率需要递归地搜索兼容的包版本,确保满足请求的需求,并且请求的包的需求是兼容的。
依赖项
大多数项目和软件包都有依赖项。依赖项是其他必要的软件包,以便当前软件包能够正常工作。一个软件包将其依赖项定义为 需求,大致是软件包名称和可接受版本的组合。当前项目定义的依赖项称为 直接依赖项。当前项目每个依赖项添加的依赖项称为 间接依赖项 或 传递依赖项。
注意:有关依赖项的详细信息,请参阅 Python 打包文档中的依赖项指定器页面。
基本示例
为了帮助演示解决过程,考虑以下依赖关系:
- 项目依赖于
foo
和bar
。 foo
有一个版本,1.0.0:foo 1.0.0
依赖于lib>=1.0.0
。
bar
有一个版本,1.0.0:bar 1.0.0
依赖于lib>=2.0.0
。
lib
有两个版本,1.0.0 和 2.0.0。这两个版本都没有依赖关系。
在这个例子中,解析器必须找到一组满足项目要求的软件包版本。由于 foo
和 bar
都只有一个版本,所以将使用这些版本。解析还必须包括传递依赖项,因此必须选择一个 lib
的版本。foo 1.0.0
允许所有可用的 lib
版本,但 bar 1.0.0
需要 lib>=2.0.0
,所以必须使用 lib 2.0.0
。
在某些决议中,可能会有多个有效解决方案。考虑以下依赖关系:
该项目依赖于
foo
和bar
。foo
有两个版本,1.0.0 和 2.0.0:foo 1.0.0
没有依赖项。foo 2.0.0
依赖于lib==2.0.0
.bar
有两个版本,1.0.0 和 2.0.0:bar 1.0.0
没有依赖项。bar 2.0.0
依赖于lib==1.0.0
lib
有两个版本,1.0.0 和 2.0.0。这两个版本都没有依赖项。
在这个例子中,必须选择 foo
和 bar
的某个版本;然而,确定哪个版本需要考虑 foo
和 bar
每个版本的依赖关系。foo 2.0.0
和 bar 2.0.0
不能一起安装,因为它们在所需的 lib
版本上存在冲突,所以解析器必须选择 foo 1.0.0
(以及 bar 2.0.0
)或 bar 1.0.0
(以及 foo 1.0.0
)。这两种解决方案都是有效的,不同的解析算法可能会产生任一结果。
平台标记
标记允许将一个表达式附加到要求中,以指示何时应使用依赖项。例如 bar ; python_version < "3.9"
表示 bar
只应安装在 Python 3.8 及更早版本上。
标记用于根据当前环境或平台调整包的依赖项。例如,可以使用标记根据操作系统、CPU 架构、Python 版本、Python 实现 等 修改依赖项。
注意:有关标记的更多详细信息,请参阅 Python 打包文档中的环境标记部分。
标记对于解析非常重要,因为它们的值会改变所需的依赖。通常,Python包解析器使用当前平台的标记来确定使用哪些依赖,因为包通常是在当前平台上安装的。然而,对于锁定依赖项来说,这是一个问题——锁定文件只为在创建锁定文件的同平台上使用的开发者工作。为了解决这个问题,存在平台无关的,或者称为“通用”的解析器。
uv 支持平台特定 平台特定 和 通用 分辨率。
平台特定解析
默认情况下,uv 的 pip 接口,即 uv pip compile
,生成的解析结果是平台特定的,类似于 pip-tools
。在 uv 的项目接口中无法使用平台特定解析。
uv 还支持使用 --python-platform
和 --python-version
选项解析特定、替代平台和 Python 版本。例如,如果在使用 macOS 上的 Python 3.12,可以使用 uv pip compile --python-platform linux --python-version 3.10 requirements.in
来生成针对 Linux 上 Python 3.10 的解析结果。与通用解析不同,在平台特定解析期间,提供的 --python-version
是要使用的确切 Python 版本,而不是下限。
注意:Python的环境标记比简单的--python-platform
参数能暴露出更多关于当前机器的信息。例如,macOS上的platform_version
标记包括内核构建的时间,这在理论上可以编码在包需求中。uv的解析器会尽力生成一个与在目标--python-platform
上运行的任何机器兼容的解析,这对于大多数用例应该是足够的,但对于复杂的包和平台组合可能会丢失精度。
全局解析
uv 的锁文件 (uv.lock
) 使用全局解析创建,可以在各个平台上移植。这确保了依赖项对所有参与项目工作的人都被锁定,无论操作系统、架构和 Python 版本如何。uv 锁文件由 uv lock
、uv sync
和 uv add
等项目命令创建和修改。
全局解析也适用于 uv 的 pip 接口,即 uv pip compile,使用 --universal
标志。生成的需求文件将包含标记,指示每个依赖项相关的平台。
在全局解析过程中,如果针对不同的平台需要不同的版本,一个包可能会被列出多次,具有不同的版本或 URL。标记确定将使用哪个版本。全局解析通常比特定平台的解析更具约束性,因为我们需要考虑所有标记的需求。
在全局解析过程中,所有必需的包必须与 pyproject.toml
中声明的 requires-python
的整个范围兼容。例如,如果项目的 requires-python
是 >=3.8
,则如果所有给定依赖项的版本都需要 Python 3.9 或更高版本,解析将失败,因为该依赖项缺乏适用于(例如)Python 3.8(项目支持的最低版本)的可用版本。换句话说,项目的 requires-python
必须是其所有依赖项 requires-python
的子集。
在为给定依赖项选择兼容版本时,uv 将 (默认情况下) 尝试为每个支持的 Python 版本选择每个支持的 Python 版本的最新兼容版本。例如,如果项目的 requires-python
是 >=3.8
,并且一个依赖项的最新版本需要 Python 3.9 或更高版本,而所有之前的版本都支持 Python 3.8,则解析器将为运行 Python 3.9 或更高版本的用户选择最新版本,并为运行 Python 3.8 的用户选择之前的版本。
在评估依赖项的 requires-python
范围时,uv 只考虑下限并完全忽略上限。例如,>=3.8, <4
被视为 >=3.8
。尊重 requires-python
的上限往往会导致形式上正确但实际不正确的解析,例如,解析器会回溯到第一个省略上限的已发布版本(参见:Requires-Python
上限)。
限制分辨率环境
默认情况下,通用解析器尝试解决所有平台和 Python 版本。
如果你的项目只支持有限的一组平台或 Python 版本,你可以通过 environments
设置来限制解析平台集,该设置接受一个 PEP 508 环境标记 列表。换句话说,你可以使用 environments
设置来 减少 支持的平台集。
例如,要将锁文件限制在 macOS 和 Linux 上,并避免为 Windows 解决:
pyproject.toml
[tool.uv]
environments = [
"sys_platform == 'darwin'",
"sys_platform == 'linux'",
]
或者,为了避免为替代 Python 实现:
pyproject.toml
[tool.uv]
environments = [
"implementation_name == 'cpython'"
]
环境设置中的条目必须是互斥的(即它们不能重叠)。例如,sys_platform == 'darwin'
和 sys_platform == 'linux'
是互斥的,但 sys_platform == 'darwin'
和 python_version >= '3.9'
不是,因为它们可能同时为真。
必需的环境
在 Python 生态系统中,软件包可以以源分布、构建分布(wheels)或两者兼有的方式发布;但安装一个软件包需要构建分布。如果一个软件包缺少构建分布,或者缺少针对当前平台或 Python 版本的分布(构建分布通常是特定平台的),uv 将尝试从源代码构建该软件包,然后安装生成的构建分布。
一些软件包(如 PyTorch)发布构建分布,但省略了源分布。这样的软件包 仅 可以在提供构建分布的平台上进行安装。例如,如果一个软件包为 Linux 发布构建分布,但不为 macOS 或 Windows 发布,那么该软件包 仅 可以在 Windows 上安装。
缺少源分布的软件包会导致通用解析出现问题,因为通常至少会有一个平台或 Python 版本,该软件包在这些平台上无法安装。
默认情况下,uv 要求每个这样的软件包至少包含一个与目标 Python 版本兼容的 wheel。可以使用 required-environments
设置来确保生成的解析包含特定平台的 wheels,或者在不可用的情况下失败。该设置接受一个 PEP 508 环境标记 列表。
虽然 environments
设置 限制 uv 在解析依赖关系时考虑的环境集,但 required-environments
扩展 uv 在解析依赖关系时必须支持的平台的集。
例如,environments = ["sys_platform == 'darwin'"]
将限制 uv 仅解决 macOS(并忽略 Linux 和 Windows)。另一方面,required-environments = ["sys_platform == 'darwin'"]
将 要求 任何缺少源分布的软件包包含一个适用于 macOS 的 wheel 才能安装(如果不可用,则失败)。
在实践中,required-environments
可以用于声明对非最新平台的显式支持,因为这通常需要回溯到那些软件包的最新发布版本。例如,为了确保任何仅包含构建分布的软件包都包含对 Intel macOS 的支持:
pyproject.toml
[tool.uv]
required-environments = [
"sys_platform == 'darwin' and platform_machine == 'x86_64'"
]
依赖项偏好
如果存在解析输出文件,即 uv 锁文件 (uv.lock
) 或需求输出文件 (requirements.txt
),uv 将 优先 使用那里列出的依赖项版本。同样,如果在虚拟环境中安装包,uv 将优先使用已安装的版本(如果存在)。这意味着,除非请求了不兼容的版本或明确使用 --upgrade
请求升级,否则锁定或安装的版本不会更改。
解决方案策略
默认情况下,uv 尝试使用每个包的最新版本。例如,uv pip install flask>=2.0.0
将安装 Flask 的最新版本,例如 3.0.0。如果 flask>=2.0.0
是项目的依赖项,则只会使用 Flask 3.0.0。这很重要,例如,因为运行测试将不会检查项目是否实际上与其声明的 flask
2.0.0 的最低版本兼容。
使用 --resolution lowest
,uv 将为所有依赖项安装最低可能的版本,包括直接和间接(传递性)依赖项。或者,--resolution lowest-direct
将使用所有直接依赖项的最低兼容版本,同时使用所有其他依赖项的最新兼容版本。uv 总是使用最新版本为构建依赖项。
例如,给定以下 requirements.in
文件:
requirements.in
flask>=2.0.0
运行 uv pip compile requirements.in
将生成以下 requirements.txt
文件:
requirements.txt
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in
blinker==1.7.0
# via flask
click==8.1.7
# via flask
flask==3.0.0
itsdangerous==2.1.2
# via flask
jinja2==3.1.2
# via flask
markupsafe==2.1.3
# via
# jinja2
# werkzeug
werkzeug==3.0.1
# via flask
然而,uv pip compile --resolution lowest requirements.in
将会生成:
requirements.in
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in --resolution lowest
click==7.1.2
# via flask
flask==2.0.0
itsdangerous==2.0.0
# via flask
jinja2==3.0.0
# via flask
markupsafe==2.0.0
# via jinja2
werkzeug==2.0.0
# via flask
发布库时,建议在持续集成中单独运行带有 --resolution lowest
或 --resolution lowest-direct
的测试,以确保与声明的较低界限兼容。
预发布版本处理
默认情况下,uv 在依赖项解析过程中会在两种情况下接受预发布版本:
- 如果包是一个直接依赖项,并且其版本指定符包括一个预发布指定符(例如,
flask>=2.0.0rc1
)。 - 如果 所有 发布的版本都是预发布版本。
如果由于传递性预发布版本导致依赖项解析失败,uv 将提示使用 --prerelease allow
以允许所有依赖项使用预发布版本。
或者,可以将传递性依赖项添加为 约束 或直接依赖项(即在 requirements.in
或 pyproject.toml
中),并使用预发布版本指定符(例如,flask>=2.0.0rc1
)来选择性地为该特定依赖项启用预发布支持。
预发布版本 众所周知很难 进行建模,并且是其他打包工具中频繁出现错误的原因。uv 的预发布处理是 有意 限制的,并且需要用户选择性地启用预发布版本以确保正确性。
有关更多详细信息,请参阅 预发布兼容性。
多版本解析
在全局解析过程中,一个包可能在同一锁定文件中多次列出,具有不同的版本或URL,因为不同平台或Python版本可能需要不同的版本。
可以使用 --fork-strategy
设置来控制uv如何在(1)最小化所选版本数量和(2)为每个平台选择最新可能版本之间进行权衡。前者会导致跨平台的一致性更高,而后者则会导致尽可能使用较新的包版本。
默认情况下(--fork-strategy requires-python
),uv将优化选择每个支持Python版本的每个包的最新版本,同时最小化跨平台所选版本的数量。
例如,当解析具有Python要求 >=3.8
的 numpy
时,uv将选择以下版本:
numpy==1.24.4 ; python_version == "3.8"
numpy==2.0.2 ; python_version == "3.9"
numpy==2.2.0 ; python_version >= "3.10"
此决议反映了以下事实:NumPy 2.2.0 及更高版本至少需要 Python 3.10,而早期版本与 Python 3.8 和 3.9 兼容。
在 --fork-strategy fewest
下,uv 将会最小化每个包所选版本的数量,优先选择与更广泛的受支持 Python 版本或平台兼容的较旧版本。
例如,在上面的场景中,uv 会为所有 Python 版本选择 numpy==1.24.4
,而不是将 Python 3.9 升级到 numpy==2.0.2
,将 Python 3.10 及以后的版本升级到 numpy==2.2.0
。
依赖约束
与pip类似,uv支持约束文件(--constraint constraints.txt
),这可以缩小给定包可接受版本的集合。约束文件类似于需求文件,但仅作为约束列出并不会导致包被包含在解决过程中。相反,只有当请求的包已经被作为直接或间接依赖项拉入时,约束才会生效。约束对于减少间接依赖项的可用版本范围很有用。它们还可以用于保持解决过程与其他一些已解决版本集同步,无论两个集合之间重叠的包是哪些。
依赖项覆盖
依赖项覆盖允许通过覆盖包声明的依赖项来绕过不成功或不理想的解析。覆盖在你知道某个依赖项与包的某个版本兼容,尽管元数据表明否则的情况下非常有用。
例如,如果一个传递依赖项声明了要求 pydantic>=1.0,<2.0
,但确实与 pydantic>=2.0
一起工作,用户可以通过在覆盖中包含 pydantic>=1.0,<3
来覆盖声明的依赖项,从而允许解析器选择 pydantic
的新版本。
具体来说,如果 pydantic>=1.0,<3
被包含为覆盖项,uv 将忽略对 pydantic
的所有声明要求,用覆盖项替换它们。在上面的例子中,pydantic>=1.0,<2.0
要求将被完全忽略,并替换为 pydantic>=1.0,<3
。
虽然约束只能 减少 包可接受版本的集合,但覆盖可以 扩展 可接受版本的集合,为错误的上限版本提供逃生门。与约束一样,覆盖不会添加对包的依赖,并且仅在直接或传递依赖项请求包时才生效。
在 pyproject.toml
中,使用 tool.uv.override-dependencies
来定义覆盖项的列表。在兼容 pip 的接口中,可以使用 --override
选项传递与约束文件格式相同的文件。
如果为同一包提供了多个覆盖项,必须使用 标记 来区分它们。如果一个包有带有标记的依赖项,在使用覆盖时将无条件替换它——无论标记评估为真或假都无关紧要。
依赖元数据
在解析过程中,uv 需要解析它遇到的每个包的元数据,以确定其依赖项。此元数据通常作为包索引中的静态文件提供;然而,对于仅提供源分布的包,可能无法提前获得元数据。
在这种情况下,uv 必须构建该包以确定其元数据(例如,通过调用 setup.py
)。这可能会在解析过程中引入性能惩罚。此外,它还要求该包可以在所有平台上构建,这可能并不成立。
例如,您可能有一个仅应在 Linux 上构建和安装的包,但在 macOS 或 Windows 上无法成功构建。虽然 uv 可以为这种情况构造一个完全有效的锁文件,但这样做需要构建该包,这将导致在非 Linux 平台上失败。
tool.uv.dependency-metadata
表可以用来提前提供此类依赖项的静态元数据,从而允许 uv 跳过构建步骤并使用提供的元数据。
例如,为了提前提供 chumpy
的元数据,请将其 dependency-metadata
包含在 pyproject.toml
中:
[[tool.uv.dependency-metadata]]
name = "chumpy"
version = "0.70"
requires-dist = ["numpy>=1.8.1", "scipy>=0.13.0", "six>=1.11.0"]
这些声明旨在用于那些不在最初声明静态元数据的包的情况,尽管它们对于需要禁用构建隔离的包也很有用。在这种情况下,提前声明包元数据可能比在解决包之前创建自定义构建环境更容易。
例如,您可以为 flash-attn
声明元数据,这样 uv 就可以在不从源代码构建包的情况下进行解析(这本身需要安装 torch
):
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["flash-attn"]
[tool.uv.sources]
flash-attn = { git = "https://github.com/Dao-AILab/flash-attention", tag = "v2.6.3" }
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
与依赖覆盖类似,tool.uv.dependency-metadata
也可以用于处理包的元数据不正确或不完整,或者包在包索引中不可用的情况。虽然依赖覆盖允许全局覆盖包的允许版本,但元数据覆盖允许覆盖特定包声明的元数据。
注意:在 tool.uv.dependency-metadata
中的 version
字段对于基于注册表的依赖项是可选的(如果省略,uv 将假设元数据适用于该软件包的所有版本),但对于直接 URL 依赖项(如 Git 依赖项)则是必需的。
tool.uv.dependency-metadata
表中的条目遵循 Metadata 2.3 规范,尽管 uv 只读取 name
、version
、requires-dist
、requires-python
和 provides-extra
。version
字段也被认为是可选的。如果省略,则将使用指定软件包的所有版本的元数据。
下限
默认情况下,uv add
会为依赖项添加下限,并且当使用 uv 管理项目时,如果直接依赖项没有下限,uv 将会发出警告。
在下限不是关键路径的情况下,下限并不是必须的,但在存在依赖项冲突的情况下,它们非常重要。例如,考虑一个需要两个包的项目,并且这两个包有冲突的依赖项。解析器需要检查两个包的所有版本组合,如果所有版本都冲突,则会报告错误,因为依赖项无法满足。如果没有下限,解析器可以(并且通常会的)回溯到包的最老版本。这不仅因为它是慢的,老版本的包通常无法构建,或者解析器可能会选择一个足够旧的版本,以至于它不依赖于冲突的包,但也不与您的代码兼容,这也是一个问题。
当编写库时,下限尤为重要。声明您的库可以与之工作的每个依赖项的最低版本,并验证这些界限是否正确——可以通过测试 --resolution lowest
或 --resolution lowest-direct
(https://docs.astral.sh/uv/concepts/resolution/#resolution-strategy)来验证。否则,用户可能会收到您库的一个依赖项的老旧、不兼容版本,并且库会因意外错误而失败。
可复现的解决方案
uv 支持一个 --exclude-newer
选项,可以将解析限制在发布日期早于特定日期的分发版本上,从而允许在不考虑新包发布的情况下复现安装。日期可以指定为 RFC 3339 时间戳(例如,2006-12-02T02:07:43Z
)或系统配置时区中的相同格式的本地日期(例如,2006-12-02
)。
请注意,包索引必须支持在 PEP 700 中指定的 upload-time
字段。如果给定分发的字段不存在,则该分发将被视为不可用。PyPI 为所有包提供 upload-time
。
为了确保可复现性,不满足解析的消息将不会提及由于 --exclude-newer
标志而排除了分发——新的分发将被视为不存在。
注意:--exclude-newer
选项仅适用于从注册表(例如,与 Git 依赖项相对)中读取的软件包。此外,当使用 uv pip
接口时,uv 不会降级之前已安装的软件包,除非提供了 --reinstall
标志,在这种情况下,uv 将执行新的解析。
源代码分发
PEP 625 规定,软件包必须以 gzip tarball (.tar.gz
) 归档文件的形式分发源代码分发。在此规范之前,其他需要为向后兼容性而支持的归档格式也被允许。uv 支持以下格式的归档文件的读取和提取:
- gzip tarball (
.tar.gz
,.tgz
) - bzip2 tarball (
.tar.bz2
,.tbz
) - xz tarball (
.tar.xz
,.txz
) - zstd tarball (
.tar.zst
) - lzip tarball (
.tar.lz
) - lzma tarball (
.tar.lzma
) - zip (
.zip
)
了解更多
有关解析器内部细节的更多信息,请参阅 解析器参考 文档。
缓存
依赖缓存
uv 使用积极的缓存策略来避免重新下载(和重新构建)之前运行中已访问过的依赖项。
uv 的缓存语义的具体细节取决于依赖项的性质:
- 对于注册表依赖项(如从 PyPI 下载的依赖项),uv 尊重 HTTP 缓存头。
- 对于直接 URL 依赖项,uv 尊重 HTTP 缓存头,并且根据 URL 本身进行缓存。
- 对于 Git 依赖项,uv 根据完全解析的 Git 提交哈希进行缓存。因此,
uv pip compile
将 Git 依赖项固定到特定的提交哈希,当写入解析后的依赖项集时。 - 对于本地依赖项,uv 根据源存档的最后修改时间进行缓存(即本地的
.whl
或.tar.gz
文件)。对于目录,uv 根据最后一个修改的pyproject.toml
、setup.py
或setup.cfg
文件进行缓存。
如果您遇到缓存问题,uv 包含几个逃生门:
- 要强制 uv 重新验证所有依赖项的缓存数据,向任何命令传递
--refresh
(例如,uv sync --refresh
或uv pip install --refresh ...
)。 - 要强制 uv 重新验证特定依赖项的缓存数据,向任何命令传递
--refresh-package
(例如,uv sync --refresh-package flask
或uv pip install --refresh-package flask ...
)。 - 要强制 uv 忽略现有的已安装版本,向任何安装命令传递
--reinstall
(例如,uv sync --reinstall
或uv pip install --reinstall ...
)。
作为一个特殊情况,uv 将始终重新构建并重新安装命令行上明确传递的任何本地目录依赖项(例如,uv pip install .
)。
动态元数据
默认情况下,uv 将仅在目录根目录中的 pyproject.toml
、setup.py
或 setup.cfg
文件发生变化,或者添加或删除了 src
目录时,才会重新构建和重新安装本地目录依赖项(例如,可编辑项)。这是一个启发式方法,在某些情况下可能会导致比期望的更少的重新安装。
为了将附加信息纳入特定软件包的缓存键中,您可以在 tool.uv.cache-keys
下添加缓存键条目,这涵盖了文件路径和 Git 提交哈希。设置 tool.uv.cache-keys
将替换默认值,因此任何必要的文件(如 pyproject.toml
)仍应包含在用户定义的缓存键中。
例如,如果项目在 pyproject.toml
中指定了依赖项,但使用 setuptools-scm
来管理其版本,因此应该在提交哈希或依赖项发生变化时重新构建,您可以将以下内容添加到项目的 pyproject.toml
中:
pyproject.toml
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true } }]
如果您动态元数据包含来自Git标签集的信息,您可以将缓存密钥扩展以包含标签:
pyproject.toml
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true, tags = true } }]
同样,如果一个项目从 requirements.txt
读取以填充其依赖项,你可以在项目的 pyproject.toml
中添加以下内容:
pyproject.toml
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }]
全局匹配符支持用于 file
键,遵循 glob
crate 的语法 glob。例如,要使缓存失效,每次项目目录或其任何子目录中的 .toml
文件被修改时,请使用以下命令:
pyproject.toml
[tool.uv]
cache-keys = [{ file = "**/*.toml" }]
注意:使用通配符可能很昂贵,因为 uv 可能需要遍历文件系统以确定是否有任何文件已更改。这反过来又可能需要遍历大型或深度嵌套的目录。
同样,如果项目依赖于环境变量,您可以将以下内容添加到项目的 pyproject.toml
中,以便在环境变量更改时使缓存失效:
pyproject.toml
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { env = "MY_ENV_VAR" }]
最后,为了在创建或删除特定目录(如 src
)时使项目失效,将以下内容添加到项目的 pyproject.toml
文件中:
pyproject.toml
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { dir = "src" }]
请注意,dir
键只会跟踪目录本身的更改,而不会跟踪目录内的任意更改。
作为一个逃生口,如果一个项目使用 dynamic
元数据,且不被 tool.uv.cache-keys
覆盖,你可以通过将项目添加到 tool.uv.reinstall-package
列表中来指示 uv 始终 重建和重新安装它:
pyproject.toml
[tool.uv]
reinstall-package = ["my-package"]
这将强制 uv 在每次运行时重新构建和重新安装 my-package
,无论该包的 pyproject.toml
、setup.py
或 setup.cfg
文件是否已更改。
缓存安全
可以安全地并发运行多个 uv 命令,即使是对同一个虚拟环境。uv 的缓存设计为线程安全且仅可追加,因此对多个并发读取者和写入者非常健壮。uv 在安装时会将对目标虚拟环境的文件锁定,以避免跨进程的并发修改。
请注意,当其他 uv 命令正在运行时,不安全修改 uv 缓存(例如,uv cache clean
),并且绝对不安全直接修改缓存(例如,通过删除文件或目录)。
清除缓存
uv 提供了几种不同的机制来从缓存中删除条目:
uv cache clean
从缓存目录中删除 所有 缓存条目,将其完全清除。uv cache clean ruff
删除ruff
包的所有缓存条目,这对于使单个或有限集的包的缓存无效很有用。uv cache prune
删除所有 未使用 的缓存条目。例如,缓存目录可能包含在之前的 uv 版本中创建的条目,这些条目现在不再必要,可以安全地删除。uv cache prune
可以定期运行,以保持缓存目录的清洁。
持续集成中的缓存
在持续集成环境中(如GitHub Actions或GitLab CI)缓存包安装工件是一种常见的做法,以加快后续运行的效率。
默认情况下,uv会缓存从源代码构建的wheel文件以及直接下载的预构建wheel文件,以实现高性能的包安装。
然而,在持续集成环境中,保留预构建的wheel文件可能并不理想。使用uv时,我们发现通常从缓存中省略预构建的wheel文件(并在每次运行时从注册表重新下载它们)会更快。另一方面,缓存从源代码构建的wheel文件通常是有价值的,因为wheel构建过程可能成本较高,尤其是对于扩展模块。
为了支持这种缓存策略,uv提供了一个uv cache prune --ci
命令,该命令会从缓存中删除所有预构建的wheel文件和未解压的源代码分发,但保留任何从源代码构建的wheel文件。我们建议在您的持续集成作业结束时运行uv cache prune --ci
,以确保最大的缓存效率。有关示例,请参阅GitHub集成指南。
缓存目录
uv 根据以下顺序确定缓存目录:
- 如果请求了
--no-cache
,则使用临时缓存目录。 - 通过
--cache-dir
、UV_CACHE_DIR
或 tool.uv.cache-dir 指定的特定缓存目录。 - 系统适当的缓存目录,例如,在 Unix 上是
$XDG_CACHE_HOME/uv
或$HOME/.cache/uv
,在 Windows 上是%LOCALAPPDATA%\uv\cache
。
注意:uv 总是需要一个缓存目录。当请求 --no-cache
时,uv 仍然会使用一个临时缓存来在该单个调用内部共享数据。
在大多数情况下,应使用 --refresh
而不是 --no-cache
——因为它将更新后续操作的缓存,但不会从缓存中读取。
缓存目录应位于 uv 运行的 Python 环境所在的同一文件系统上,这对于性能非常重要。否则,uv 将无法将缓存中的文件链接到环境中,而需要回退到缓慢的复制操作。
2025-03-24(一)