配置项目
Python 版本要求
项目可以在 pyproject.toml 的 project.requires-python 字段中声明其支持的 Python 版本。
建议设置 requires-python 值
Python 版本要求决定了项目中允许使用的 Python 语法,并影响依赖版本的选择(它们必须支持相同的 Python 版本范围)。
入口点 (Entry points)
入口点 (Entry points) 是已安装包声明接口的官方术语。这些接口包括
重要
使用入口点表需要定义一个 构建系统。
命令行接口 (CLI)
项目可以在 pyproject.toml 的 [project.scripts] 表中定义项目的命令行接口 (CLI)。
例如,要声明一个名为 hello 的命令,该命令调用 example 模块中的 hello 函数
然后,该命令即可从控制台运行
图形用户界面 (GUI)
项目可以在 pyproject.toml 的 [project.gui-scripts] 表中定义项目的图形用户界面 (GUI)。
重要
这些与 命令行接口 的区别仅在于 Windows 平台上,它们会被 GUI 可执行文件包裹,从而无需控制台即可启动。在其他平台上,它们的行为相同。
例如,要声明一个名为 hello 的命令,该命令调用 example 模块中的 app 函数
插件入口点
项目可以在 pyproject.toml 的 [project.entry-points] 表中定义用于插件发现的入口点。
例如,将 example-plugin-a 包注册为 example 的插件
然后,在 example 中,可以通过以下方式加载插件
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 将构建并将项目安装到项目环境中。
可以向 uv init 提供 --build-backend 选项,以创建具有合适布局的打包项目。可以向 uv init 提供 --package 选项,以使用默认构建系统创建打包项目。
注意
虽然 uv 在没有构建系统定义的情况下不会构建和安装当前项目,但在其他包中并不强制要求存在 [build-system] 表。出于遗留原因,如果未定义构建系统,则使用 setuptools.build_meta:__legacy__ 来构建包。您依赖的包可能没有显式声明其构建系统,但仍然可以安装。同样,如果您 添加了对本地项目的依赖 或使用 uv pip 安装它,uv 将尝试构建并安装它,无论是否存在 [build-system] 表。
构建系统用于支持以下功能
- 从分发包中包含或排除文件
- 可编辑安装行为
- 动态项目元数据
- 原生代码编译
- 打包共享库
要配置这些功能,请参阅您所选构建系统的文档。
项目打包
正如 构建系统 中所讨论的,Python 项目必须经过构建才能安装。这个过程通常被称为“打包”。
如果您需要执行以下操作,可能需要一个包
- 向项目添加命令
- 将项目分发给他人
- 使用
src和test布局 - 编写一个库
如果您正在执行以下操作,则可能 不需要 包
- 编写脚本
- 构建一个简单的应用程序
- 使用扁平布局
虽然 uv 通常使用 构建系统 的声明来确定项目是否应被打包,但 uv 也允许通过 tool.uv.package 设置覆盖此行为。
设置 tool.uv.package = true 将强制项目构建并安装到项目环境中。如果未定义构建系统,uv 将使用 setuptools 的遗留后端。
设置 tool.uv.package = false 将强制项目包 不 被构建并安装到项目环境中。当与项目交互时,uv 将忽略已声明的构建系统;但是,uv 仍会尊重明确的构建请求,例如调用 uv build。
项目环境路径
UV_PROJECT_ENVIRONMENT 环境变量可用于配置项目虚拟环境路径(默认为 .venv)。
如果提供了相对路径,它将相对于工作区根目录进行解析。如果提供了绝对路径,它将按原样使用,即不会为环境创建子目录。如果所提供的路径下不存在环境,uv 将会创建它。
此选项可用于写入系统 Python 环境,但不建议这样做。uv sync 默认会从环境中删除多余的包,因此可能会导致系统处于损坏状态。
要定位系统环境,请将 UV_PROJECT_ENVIRONMENT 设置为 Python 安装的前缀。例如,在基于 Debian 的系统上,这通常是 /usr/local
要定位此环境,您可以执行 export UV_PROJECT_ENVIRONMENT=/usr/local。
重要
如果提供了绝对路径且该设置跨多个项目使用,则环境会被每个项目中的调用覆盖。此设置仅建议在 CI 或 Docker 映像中针对单个项目使用。
注意
默认情况下,uv 在执行项目操作时不会读取 VIRTUAL_ENV 环境变量。如果 VIRTUAL_ENV 设置为与项目环境不同的路径,将会显示警告。可以使用 --active 标志来选择尊重 VIRTUAL_ENV。可以使用 --no-active 标志来禁止显示该警告。
构建隔离
根据 PEP 517,默认情况下,uv 会在独立的虚拟环境中构建所有包及其声明的构建依赖项。
有些包与这种构建隔离方法不兼容,无论是有意还是无意。
例如,像 flash-attn 和 deepspeed 这样的包需要针对安装在项目环境中的同一版本 PyTorch 进行构建;通过在隔离环境中构建它们,它们可能会无意中针对不同版本的 PyTorch 进行构建,从而导致运行时错误。
在其他情况下,包可能会不小心在其声明的构建依赖列表中遗漏了必要的依赖项。例如,cchardet 要求在安装 cchardet 之前在项目环境中安装 cython,但并未将其声明为构建依赖项。
为了解决这些问题,uv 支持两种修改构建隔离行为的独立方法
-
增强构建依赖列表:这允许您在隔离环境中安装包,但附带包本身未通过
extra-build-dependencies设置声明的其他构建依赖项。对于像flash-attn这样的包,您甚至可以强制这些构建依赖项(如torch)与项目环境中已安装或将要安装的包版本相匹配。 -
针对特定包禁用构建隔离:这允许您在不进行构建隔离的情况下安装包。
在可能的情况下,我们建议增强构建依赖项,而不是完全禁用构建隔离。因为后者要求在安装包本身之前,必须先在项目环境中安装好构建依赖项,这可能导致安装步骤变得复杂,环境包含冗余包,并增加在其他上下文中复现项目环境的难度。
增强构建依赖
要为特定包增强构建依赖列表,请将其添加到 pyproject.toml 中的 extra-build-dependencies 列表中。
例如,要将 cython 作为额外构建依赖项来构建 cchardet,请在您的 pyproject.toml 中包含以下内容
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet"]
[tool.uv.extra-build-dependencies]
cchardet = ["cython"]
为了确保构建依赖项与项目环境中已安装或将要安装的包版本匹配,请在 extra-build-dependencies 表中设置 match-runtime = true。例如,要将 torch 作为额外构建依赖项来构建 deepspeed,请在您的 pyproject.toml 中包含以下内容
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["deepspeed", "torch"]
[tool.uv.extra-build-dependencies]
deepspeed = [{ requirement = "torch", match-runtime = true }]
这将确保 deepspeed 构建时所使用的 torch 版本与项目环境中安装的版本相同。
同样,要将 torch 作为额外构建依赖项来构建 flash-attn,请在您的 pyproject.toml 中包含以下内容
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["flash-attn", "torch"]
[tool.uv.extra-build-dependencies]
flash-attn = [{ requirement = "torch", match-runtime = true }]
[tool.uv.extra-build-variables]
flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" }
注意
环境变量 FLASH_ATTENTION_SKIP_CUDA_BUILD 可确保从兼容的预构建 wheel 安装 flash-attn,而不是尝试从源代码构建(这需要访问 CUDA 开发工具包)。如果 CUDA 工具包不可用,可以忽略此环境变量;如果当前平台、Python 版本和 PyTorch 版本有可用的预构建 wheel,flash-attn 将会从预构建 wheel 安装。
同样,deep_gemm 也遵循相同的模式
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["deep_gemm", "torch"]
[tool.uv.sources]
deep_gemm = { git = "https://github.com/deepseek-ai/DeepGEMM" }
[tool.uv.extra-build-dependencies]
deep_gemm = [{ requirement = "torch", match-runtime = true }]
extra-build-dependencies 和 extra-build-variables 的使用情况会被 uv 缓存追踪,因此更改这些设置将触发受影响包的重新安装和重新构建。例如,在 flash-attn 的情况下,升级项目中使用的 torch 版本随后会触发 flash-attn 使用新版本 torch 的重新构建。
动态元数据
match-runtime = true 仅适用于像 flash-attn 这样声明了静态元数据的包。如果静态元数据不可用,uv 需要在依赖解析阶段构建包;因此,uv 无法确定最终会安装在项目环境中的构建依赖项版本。
换句话说,如果 flash-attn 没有声明静态元数据,uv 将无法确定安装在项目环境中的 torch 版本,因为它需要在解析 torch 版本之前构建 flash-attn。
作为一个具体示例,axolotl 是一个流行的包,它需要增强的构建依赖项,但没有声明静态元数据,因为该包的依赖项会根据项目环境中安装的 torch 版本而变化。在这种情况下,用户应该改为指定他们计划在项目中使用的确切 torch 版本,然后用该版本来增强构建依赖项。
例如,要针对 torch==2.6.0 构建 axolotl,请在您的 pyproject.toml 中包含以下内容
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["axolotl[deepspeed, flash-attn]", "torch==2.6.0"]
[tool.uv.extra-build-dependencies]
axolotl = ["torch==2.6.0"]
deepspeed = ["torch==2.6.0"]
flash-attn = ["torch==2.6.0"]
同样,旧版本的 flash-attn 没有声明静态元数据,因此开箱即不支持 match-runtime = true。但与 axolotl 不同,flash-attn 的依赖项并不随构建环境的动态属性而变化。因此,用户可以通过 dependency-metadata 设置预先提供 flash-attn 的元数据,从而无需在依赖解析阶段构建该包。例如,预先提供 flash-attn 的元数据
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
提示
要确定像 flash-attn 这样的包的元数据,请导航到对应的 Git 仓库,或者在 PyPI 上查找并下载包的源代码分发包。包的依赖项通常可以在 setup.py 或 setup.cfg 文件中找到。
(如果包中包含已构建的分发包,您可以解压它来查找 METADATA 文件;但是,已构建分发包的存在消除了预先提供元数据的必要性,因为它已经对 uv 可见。)
tool.uv.dependency-metadata 中的 version 字段对于基于仓库的依赖项是可选的(如果省略,uv 将假定元数据适用于该包的所有版本),但对于直接 URL 依赖项(如 Git 依赖项)是 必需 的。
禁用构建隔离
在不进行构建隔离的情况下安装包,要求该包的构建依赖项 必须 在构建包本身之前安装在项目环境中。
例如,在过去,要在不进行构建隔离的情况下安装 cchardet,您首先需要在项目环境中安装 cython 和 setuptools 包,然后单独执行一次安装命令,在不进行构建隔离的情况下安装 cchardet
uv 简化了这一过程,允许您通过 pyproject.toml 中的 no-build-isolation-package 设置和命令行中的 --no-build-isolation-package 标志来指定不应在隔离中构建的包。此外,当一个包被标记为禁用构建隔离时,uv 将执行两阶段安装,首先安装任何支持构建隔离的包,然后安装那些不支持的包。因此,如果项目的构建依赖项作为项目依赖项包含在内,uv 会在安装需要禁用构建隔离的包之前自动安装它们。
例如,要在不进行构建隔离的情况下安装 cchardet,请在您的 pyproject.toml 中包含以下内容
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet", "cython", "setuptools"]
[tool.uv]
no-build-isolation-package = ["cchardet"]
运行 uv sync 时,uv 首先会在项目环境中安装 cython 和 setuptools,然后安装 cchardet(不进行构建隔离)
同样,要在不进行构建隔离的情况下安装 flash-attn,请在您的 pyproject.toml 中包含以下内容
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["flash-attn", "torch"]
[tool.uv]
no-build-isolation-package = ["flash-attn"]
运行 uv sync 时,uv 首先会在项目环境中安装 torch,然后安装 flash-attn(不进行构建隔离)。由于 torch 既是项目依赖项又是构建依赖项,因此 torch 的版本在构建环境和运行时环境之间保证是一致的。
上述方法的一个缺点是它要求在项目环境中安装构建依赖项,这对于 flash-attn(在构建时和运行时都需要 torch)是合适的,但对于 cchardet(仅在构建时需要 cython)则不然。
为了避免将构建依赖项包含在项目环境中,uv 支持一种两步安装过程,允许您将构建依赖项与需要它们的包分开。
例如,cchardet 的构建依赖项可以隔离到一个可选的 build 组中,如下所示
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet"]
[project.optional-dependencies]
build = ["setuptools", "cython"]
[tool.uv]
no-build-isolation-package = ["cchardet"]
鉴于上述情况,用户可以首先使用 build 可选组进行同步,然后再不使用它来删除构建依赖项
$ uv sync --extra build
+ cchardet==2.1.7
+ cython==3.1.3
+ setuptools==80.9.0
$ uv sync
- cython==3.1.3
- setuptools==80.9.0
有些包(如 cchardet)仅在 uv sync 的 安装 阶段需要构建依赖项。其他包则要求在 解析 阶段就需要存在构建依赖项,仅仅为了解析项目的依赖关系。
在这种情况下,可以使用低级 uv pip API 在运行任何 uv lock 或 uv sync 命令之前安装构建依赖项。例如,给定
[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
或者,用户可以通过 dependency-metadata 设置预先提供 flash-attn 的元数据,从而无需在依赖解析阶段构建该包。例如,预先提供 flash-attn 的元数据
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
可编辑模式
默认情况下,项目将以可编辑模式安装,这样对源代码的更改会立即反映在环境中。uv sync 和 uv run 都接受 --no-editable 标志,它指示 uv 以非可编辑模式安装项目。--no-editable 旨在用于部署场景(例如构建 Docker 容器),在这些场景中,项目应包含在部署环境中,且不依赖于原始源代码。
冲突依赖
uv 会同时解析所有项目依赖项,包括可选依赖项("extras")和依赖组。如果一个部分中声明的依赖项与另一个部分中的不兼容,uv 将无法解析项目的要求并报错。
uv 支持明确声明冲突的依赖组。例如,声明 optional-dependency 组 extra1 和 extra2 不兼容
或者,声明开发依赖组 group1 和 group2 不兼容
更多信息请参阅 解析文档。
受限解析环境
如果您的项目支持较小范围的平台或 Python 版本,您可以通过 environments 设置来约束已解析平台的集合,该设置接受 PEP 508 环境标记列表。例如,要将锁文件限制为 macOS 和 Linux,并排除 Windows
更多信息请参阅 解析文档。
必需环境
如果您的项目 必须 支持特定的平台或 Python 版本,您可以通过 required-environments 设置将该平台标记为必需。例如,要求项目支持 Intel macOS
[tool.uv]
required-environments = [
"sys_platform == 'darwin' and platform_machine == 'x86_64'",
]
required-environments 设置仅与不发布源代码分发包(如 PyTorch)的包相关,因为此类包 只能 安装在由该包发布的预构建二进制分发包(wheels)所涵盖的环境中。
更多信息请参阅 解析文档。