3.4.6 配置项目

原文链接: https://docs.astral.sh/uv/concepts/projects/config/

3.4.6 配置项目

Python 版本要求

项目可以在 pyproject.toml 的 project.requires-python 字段中声明它所支持的 Python 版本。

建议设置 requires-python 值:

1
2
3
4
5
# pyproject.toml
[project]
name = "example"
version = "0.1.0"
requires-python = ">=3.12"

Python 版本要求决定项目允许使用的 Python 语法,并影响依赖版本的选择(它们必须支持相同的 Python 版本范围)。

入口点

入口点是已安装包对外公布接口的正式术语,包括:

重要

使用入口点表需要定义构建系统。

命令行接口

项目可以在 pyproject.toml 的 [project.scripts] 表中为项目定义命令行接口(CLI)。

例如,声明一个名为 hello 的命令,调用 example 模块中的 hello 函数:

1
2
3
# pyproject.toml
[project.scripts]
hello = "example:hello"

之后就可以在控制台中运行该命令:

1
$ uv run hello

图形用户界面

项目可以在 pyproject.toml 的 [project.gui-scripts] 表中为项目定义图形用户界面(GUI)。

重要

它们只在 Windows 上与命令行接口不同:在 Windows 上它们会被 GUI 可执行文件包装,从而无需控制台即可启动。在其他平台上行为相同。

例如,声明一个名为 hello 的命令,调用 example 模块中的 app 函数:

1
2
3
# pyproject.toml
[project.gui-scripts]
hello = "example:app"

插件入口点

项目可以在 pyproject.toml 的 [project.entry-points] 表中定义用于插件发现的入口点。

例如,把 example-plugin-a 包注册为 example 的插件:

1
2
3
# pyproject.toml
[project.entry-points.'example.plugins']
a = "example_plugin_a"

然后,在 example 中可以这样加载插件:

1
2
3
4
5
# 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 会构建并把项目安装到项目环境。

默认情况下,uv init 使用 uv 构建后端创建可打包项目。可以提供 --build-backend 选项来选择替代构建后端,也可以提供 --no-package 来创建扁平的、不打包的项目。

注意

虽然在没有构建系统定义时 uv 不会构建和安装当前项目,但其他包并不要求必须存在 [build-system] 表。出于历史原因,如果未定义构建系统,则会使用 setuptools.build_meta:__legacy__ 来构建包。你依赖的包可能没有显式声明构建系统,但仍可安装。同样,如果你添加对本地项目的依赖或用 uv pip 安装它,无论是否存在 [build-system] 表,uv 都会尝试构建并安装它。

构建系统用于支撑以下特性:

  • 在分发包中包含或排除文件
  • 可编辑安装行为
  • 动态项目元数据
  • 原生代码编译
  • 内置共享库

要配置这些特性,请参阅你所选构建系统的文档。

项目打包

正如构建系统中所述,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:

1
2
$ python -c "import sysconfig; print(sysconfig.get_config_var('prefix'))"
/usr/local

要以该环境为目标,你应导出 UV_PROJECT_ENVIRONMENT=/usr/local。

重要

如果提供的是绝对路径,且该设置被多个项目共用,那么每个项目的调用都会覆盖该环境。该设置仅建议在 CI 或 Docker 镜像中用于单个项目。

注意

默认情况下,uv 在执行项目操作时不会读取 VIRTUAL_ENV 环境变量。如果 VIRTUAL_ENV 被设置为与项目环境不同的路径,会显示警告。--active 标志可用于选择遵循 VIRTUAL_ENV,--no-active 标志可用于消除该警告。

构建隔离

默认情况下,uv 按照 PEP 517 在隔离的虚拟环境中连同其声明的构建依赖一起构建所有包。

有些包无论是有意还是无意,都与这种构建隔离方式不兼容。

例如 flash-attn 和 deepspeed 这类包需要针对项目环境中已安装的同一个 PyTorch 版本进行构建;如果在隔离环境中构建,它们可能意外地针对不同版本的 PyTorch 构建,从而导致运行时错误。

另一些情况下,包可能意外漏掉构建依赖列表中的必需依赖。例如 cchardet 需要在安装 cchardet 之前把 cython 装到项目环境中,但它并未把 cython 声明为构建依赖。

为解决这些问题,uv 支持两种不同的方式来修改构建隔离行为:

  1. 增强构建依赖列表:这允许你在隔离环境中安装包,但附带包自身未声明的额外构建依赖,通过 extra-build-dependencies 设置实现。对于 flash-attn 这类包,你甚至可以强制这些构建依赖(例如 torch)与项目环境中已安装或将要安装的包版本一致。

  2. 为特定包禁用构建隔离:这允许你在非隔离环境中安装包。

在可能的情况下,我们建议增强构建依赖,而不是完全禁用构建隔离,因为后一种方式要求在安装包本身之前把构建依赖装到项目环境中,这会导致更复杂的安装步骤、项目环境中出现多余的包,以及在其他场景下难以复现项目环境。

增强构建依赖

要为特定包增强构建依赖列表,请把它加入 pyproject.toml 中的 extra-build-dependencies 列表。

例如,要用 cython 作为额外构建依赖来构建 cchardet,请在 pyproject.toml 中包含:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 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 中包含:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 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 版本进行构建。

提示

预构建的 deepspeed wheel 也可以从 Astral GPU 索引获取。

同样,要用 torch 作为额外构建依赖来构建 flash-attn,请在 pyproject.toml 中包含:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 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 环境变量让 flash-attn 可以从预构建 wheel 解析,而不是尝试从源码构建(后者需要 CUDA 开发工具包)。

如果解析期间 CUDA 工具包可用,我们建议省略 FLASH_ATTENTION_SKIP_CUDA_BUILD 变量,因为把 FLASH_ATTENTION_SKIP_CUDA_BUILD 设为 TRUE 时,如果目标 PyTorch 版本、GPU 版本和平台没有兼容的预构建 wheel,可能导致安装不兼容。

提示

预构建的 flash-attn wheel 也可以从 Astral GPU 索引获取。

同样,deep_gemm 遵循相同的模式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# pyproject.toml
[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 }]

提示

预构建的 deep_gemm wheel 也可以从 Astral GPU 索引获取。

extra-build-dependencies 和 extra-build-variables 的使用会记录在 uv 缓存中,因此修改这些设置会触发受影响包的重装和重建。例如对 flash-attn 而言,升级项目中 torch 的版本会随之触发用新版本 torch 重建 flash-attn。

动态元数据

match-runtime = true 仅适用于声明了静态元数据的包,例如 flash-attn。如果静态元数据不可用,uv 就必须在依赖解析阶段构建该包;因此 uv 无法确定最终会安装到项目环境中的构建依赖版本。

换句话说,如果 flash-attn 没有声明静态元数据,uv 就无法确定会安装到项目环境中的 torch 版本,因为它需要在解析 torch 版本之前先构建 flash-attn。

一个具体的例子是 axolotl:这个流行的包需要增强的构建依赖,但没有声明静态元数据,因为该包的依赖会随项目环境中安装的 torch 版本而变化。这种情况下,用户应改为指定他们打算在项目中使用的确切 torch 版本,然后用该版本增强构建依赖。

例如,要针对 torch==2.6.0 构建 axolotl,请在 pyproject.toml 中包含:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 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 元数据:

1
2
3
4
5
# pyproject.toml
[[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:

1
2
3
$ uv venv
$ uv pip install cython setuptools
$ uv pip install cchardet --no-build-isolation

uv 简化了这一过程:你可以在 pyproject.toml 中用 no-build-isolation-package 设置、或在命令行用 --no-build-isolation-package 标志指定不应在隔离环境中构建的包。此外,当某个包被标记为禁用构建隔离时,uv 会执行两阶段安装:先安装支持构建隔离的包,再安装不支持的那些。因此,如果项目的构建依赖也包含在项目依赖中,uv 会自动在安装需要禁用构建隔离的包之前先安装它们。

例如,要在非构建隔离下安装 cchardet,请在 pyproject.toml 中包含:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 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:

1
2
3
4
$ uv sync --extra build
 + cchardet==2.1.7
 + cython==3.1.3
 + setuptools==80.9.0

同样,要在非构建隔离下安装 flash-attn,请在 pyproject.toml 中包含:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 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 既是项目依赖又是构建依赖,其版本在构建环境与运行环境之间保证一致。

上述方式的一个缺点是需要把构建依赖装到项目环境中,这对 flash-attn 合适(它在构建时和运行时都需要 torch),但对 cchardet 不合适(它只在构建时需要 cython)。

为避免把构建依赖包含进项目环境,uv 支持两步安装流程,让你可以把构建依赖与需要它们的包分开。

例如,cchardet 的构建依赖可以隔离到一个可选的 build 组中,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# pyproject.toml
[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 可选组同步,然后再不带它同步以移除构建依赖:

1
2
3
4
5
6
7
$ 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 命令之前安装构建依赖。例如,给定:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 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:

1
2
3
$ uv venv
$ uv pip install torch setuptools
$ uv sync

另外,用户也可以通过 dependency-metadata 设置预先提供 flash-attn 元数据,从而免去在依赖解析阶段构建该包的需要。例如,预先提供 flash-attn 元数据:

1
2
3
4
5
# pyproject.toml
[[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 不兼容:

1
2
3
4
5
6
7
8
# pyproject.toml
[tool.uv]
conflicts = [
    [
      { extra = "extra1" },
      { extra = "extra2" },
    ],
]

或者声明开发依赖组 group1 与 group2 不兼容:

1
2
3
4
5
6
7
8
# pyproject.toml
[tool.uv]
conflicts = [
    [
      { group = "group1" },
      { group = "group2" },
    ],
]

更多内容请参阅解析文档。

受限解析环境

如果你的项目只支持有限的平台或 Python 版本,可以通过 environments 设置约束求解的平台集合,它接受一组 PEP 508 环境标记。例如,把锁文件限制为 macOS 和 Linux,排除 Windows:

1
2
3
4
5
6
# pyproject.toml
[tool.uv]
environments = [
    "sys_platform == 'darwin'",
    "sys_platform == 'linux'",
]

更多内容请参阅解析文档。

必需环境

如果你的项目必须支持特定平台或 Python 版本,可以通过 required-environments 设置把该平台标记为必需。例如,要求项目支持 Intel macOS:

1
2
3
4
5
# pyproject.toml
[tool.uv]
required-environments = [
    "sys_platform == 'darwin' and platform_machine == 'x86_64'",
]

required-environments 设置只对不发布源码分发的包(例如 PyTorch)有意义,因为这类包只能安装在该包发布的预构建二进制分发(wheel)所覆盖的环境上。

更多内容请参阅解析文档。

在 Linux 上,请把 minimum-libc-version 与 required-environments 一起使用,以选择要支持的 libc 实现和最低版本。

最后修改 September 25, 2026: 更新 (221c74c33)