缓存
依赖项缓存
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 cache clean
。要清除特定软件包的缓存,请运行uv cache clean <package-name>
。例如,uv cache clean ruff
将清除ruff
软件包的缓存。 - 要强制 uv 重新验证所有依赖项的缓存数据,请将
--refresh
传递给任何命令(例如,uv sync --refresh
或uv pip install --refresh ...
)。 - 要强制 uv 重新验证特定依赖项的缓存数据,请将
--refresh-package
传递给任何命令(例如,uv sync --refresh-package ruff
或uv pip install --refresh-package ruff ...
)。 - 要强制 uv 忽略现有的已安装版本,请将
--reinstall
传递给任何安装命令(例如,uv sync --reinstall
或uv pip install --reinstall ...
)。(考虑首先运行uv cache clean <package-name>
,以确保在重新安装之前清除缓存。)
作为一种特殊情况,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
中
如果您的动态元数据包含来自 Git 标签集的信息,您可以扩展缓存键以包含标签
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true, tags = true } }]
类似地,如果一个项目从 requirements.txt
读取以填充其依赖项,您可以将以下内容添加到项目的 pyproject.toml
中
file
键支持 Globs,遵循 glob
crate 的语法。例如,要在项目目录或其任何子目录中修改 .toml
文件时使缓存失效,请使用以下命令
注意
使用 Globs 可能会很昂贵,因为 uv 可能需要遍历文件系统以确定是否有任何文件已更改。这反过来可能需要遍历大型或深度嵌套的目录。
类似地,如果一个项目依赖于环境变量,您可以将以下内容添加到项目的 pyproject.toml
中,以在环境变量更改时使缓存失效
最后,要在创建或删除特定目录(如 src
)时使项目失效,请将以下内容添加到项目的 pyproject.toml
中
请注意,dir
键只会跟踪目录本身的更改,而不会跟踪目录中的任意更改。
作为一种紧急出口,如果一个项目使用 dynamic
元数据,而 tool.uv.cache-keys
未涵盖,您可以指示 uv 始终 重新构建和重新安装它,方法是将该项目添加到 tool.uv.reinstall-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 会缓存它从源代码构建的 wheels 和它直接下载的预构建 wheels,以实现高性能的软件包安装。
但是,在持续集成环境中,持久化预构建的 wheels 可能是不希望的。使用 uv,事实证明,从缓存中省略预构建的 wheels 通常更快(而是在每次运行时从注册表重新下载它们)。另一方面,缓存从源代码构建的 wheels 往往是值得的,因为 wheel 构建过程可能很昂贵,特别是对于扩展模块。
为了支持这种缓存策略,uv 提供了一个 uv cache prune --ci
命令,该命令从缓存中删除所有预构建的 wheels 和未压缩的源发行版,但保留从源代码构建的任何 wheels。我们建议在持续集成作业结束时运行 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 将无法将缓存中的文件链接到环境中,并且需要回退到缓慢的复制操作。
缓存版本控制
uv 缓存由许多存储桶组成(例如,用于 wheels 的存储桶、用于源发行版的存储桶、用于 Git 存储库的存储桶等等)。每个存储桶都有版本控制,因此如果某个版本包含对缓存格式的重大更改,uv 将不会尝试读取或写入不兼容的缓存存储桶。
例如,uv 0.4.13 包含对核心元数据存储桶的重大更改。因此,存储桶版本从 v12 增加到 v13。在缓存版本中,保证更改既向前兼容又向后兼容。
由于缓存格式的更改伴随着缓存版本的更改,因此多个版本的 uv 可以安全地读取和写入同一个缓存目录。但是,如果缓存版本在一对给定的 uv 版本之间发生了更改,则这些版本可能无法共享相同的底层缓存条目。
例如,为 uv 0.4.12 和 uv 0.4.13 使用单个共享缓存是安全的,尽管由于缓存版本的更改,缓存本身可能包含核心元数据存储桶中的重复条目。