- 开发工具
- 构建工具
【免费下载链接】hatch
Modern, extensible Python project management
本篇技术指南以 Hatch 项目的 docs/config/build.md 为骨架,系统讲解构建配置的核心主题:如何通过tool.hatch.build与tool.hatch.build.targets两个 TOML 配置表精确控制打包文件的选择、路径重写、构建目标与构建钩子,并结合构建后端 hatchling 的源码(config.py、plugin/interface.py)深入解析每个配置项的底层实现。读完本文,你将掌握 Hatch 构建系统的完整配置语法、文件选择策略的优先级关系、可复现构建原理、开发模式(dev mode)机制以及构建钩子的执行顺序,并能在自己的项目中直接落地使用。
构建目标的定义方式
构建目标(Build Targets)是 Hatch 构建配置的核心概念。每个构建目标对应一种分发产物,在pyproject.toml中表示为tool.hatch.build.targets下的一个节(section):
[tool.hatch.build.targets.<TARGET_NAME>]其中<TARGET_NAME>是构建器插件(builder plugin)注册的名字。Hatchling 内置了三个构建目标:wheel、sdist 和 custom,分别对应 Python wheel 包、源码发行包和自定义构建逻辑。任何第三方构建器插件(如hatch-aws、hatch-zipped-directory,见 builder 插件参考)都可以通过 hooks.py 中注册的hatch_register_builder钩子提供新的构建目标。
虽然不建议,但你也可以在tool.hatch.build表中定义全局配置,目标级配置中的同名键会覆盖全局配置。这一点在源码中体现得很明确:BuilderConfig的各个属性解析器(如ignore_vcs、skip_excluded_dirs、reproducible等)都采用同一套模式——先检查target_config中是否存在该键,存在则使用目标级配置,否则回退到build_config中的全局配置,具体可见 config.py 中skip_excluded_dirs与ignore_vcs的实现。
与 Python 打包生态兼容的构建系统声明
要让项目与更广泛的 Python 打包生态 兼容,必须在pyproject.toml顶部按 PEP 517 定义构建系统:
[build-system] requires = ["hatchling"] build-backend = "hatchling.build"这里声明的hatchling版本将用于构建所有目标。hatchling 是一个符合标准的构建后端,同时它本身也是 Hatch 的依赖项。Hatchling 对 PEP 517 和 PEP 660 的支持保证了与其他构建工具(pip、tox、CI 等)的互操作性。
文件选择(File selection)
构建的本质是把哪些文件放进分发产物。Hatch 提供了一整套由粗到细的文件选择机制,理解它们的优先级是正确配置的关键。从源码看,文件选择的核心裁决逻辑位于BuilderConfig.include_path()(config.py):一个相对路径最终能否被包含,取决于它是否是构建产物(build artifact)、是否符合artifacts模式、是否被排除、以及是否命中include模式。
VCS 忽略规则
默认情况下,Hatch 会尊重项目根目录或其父目录中找到的第一个.gitignore或.hgignore文件。将ignore-vcs设为true可禁用这一行为:
[tool.hatch.build.targets.sdist] ignore-vcs = true注意:对于.hgignore文件,只支持 glob 语法。这一约束在源码中有对应实现——load_vcs_exclusion_patterns 读取.hgignore时会解析syntax: glob指令,只有 glob 模式下的行才会被采纳,而.gitignore则整文件按 Git 模式格式(gitignore 模式格式)读取。VCS 忽略文件的查找由locate_file完成,边界条件为.git或.hg目录(vcs_exclusion_files)。另外,default_global_exclude会默认排除*.py[cdo]和dist目录(config.py)。
模式匹配:include 与 exclude
include和exclude选项可以精确指定每个构建要打包的文件,其中exclude优先级更高。每个条目都是一个 Git 风格 glob 模式(gitignore 模式格式)。
例如以下配置:
[tool.hatch.build.targets.sdist] include = [ "pkg/*.py", "/tests", ] exclude = [ "*.json", "pkg/_compat.py", ]效果是:排除所有.json扩展名文件;包含项目根下tests目录的全部内容,以及根下pkg目录中直接位于其下的所有.py文件(_compat.py除外)。/tests开头的/表示匹配仅限项目根目录,这是 Git 风格模式中"锚定"到根目录的语法。
在源码层面,include与exclude都会转换为pathspec.GitIgnoreSpec(include_spec、exclude_spec),并支持目标级覆盖全局级。所有模式条目必须是字符串,空字符串会被拒绝。另外值得注意的是:当设置了packages时,源码会为每个包自动追加/{relative_path}/形式的 include 模式,确保包目录必然被遍历。
Artifacts:绕过 VCS 忽略的产物
如果要包含被 VCS 忽略的文件(例如由 构建钩子 生成的文件),可以使用artifacts选项。它在语义上与include等价,但有两点关键差异:
exclude不影响 artifacts;- artifacts 只能用更明确的路径或
!取反操作符来排除,且使用!时,取反模式必须放在更通用模式之后。
[tool.hatch.build.targets.wheel] artifacts = [ "*.so", "*.dll", "!/foo/*.so", ]在include_path()的裁决顺序中,path_is_artifact()的判定优先级高于排除逻辑(config.py),这正是"artifacts 不受 exclude 影响"的源码依据。构建钩子运行时写入build_data["artifacts"]的路径则通过set_build_data生成build_artifact_spec生效(config.py)。
显式选择:only-include
only-include选项可阻止从项目根开始的目录遍历,只选择特定的相对路径(目录或文件)。使用该选项会忽略任何已定义的include模式:
[tool.hatch.build.targets.sdist] only-include = ["pkg", "tests/unit"]从源码看,only_include的默认值是default_only_include()或packages配置(config.py),路径必须是相对路径(以~或..开头的路径会被拒绝),且不允许重复。当设置了only_include时,recurse_selected_project_files会走recurse_explicit_files分支而不是全量遍历(plugin/interface.py),但显式选择的文件仍会经过include_path(..., explicit=True)的过滤,即 exclude 依然生效。
Packages:打包 src 布局的利器
packages选项在语义上与only-include等价(且only-include优先级更高),区别在于:发行路径会被折叠为只包含最后一个路径组件。
例如,要打包存放在src目录下的包foo:
[tool.hatch.build.targets.wheel] packages = ["src/foo"]源码中packages配置会被规范化排序(config.py),并且packages本身依赖于sources机制——packages = ["src/foo"]对wheel目标而言等价于:
[tool.hatch.build.targets.wheel] only-include = ["src/foo"] sources = ["src"]强制包含:force-include
force-include选项允许从文件系统的任意位置选择特定文件或目录,并映射到期望的相对发行路径:
[tool.hatch.build.targets.wheel.force-include] "../artifacts" = "pkg" "~/lib.h" = "pkg/lib.h"例如,项目根旁有一个包含lib.so的artifacts目录,家目录中还有一个lib.h,上面的配置会把两个文件都打包进发行版的pkg目录。使用时有以下注意点:
- 文件必须精确映射到期望路径,而不是映射到目录;
- 目录源的内容会被递归包含;
- 要将目录内容直接映射到根目录,使用
/(正斜杠); - 源不存在会直接报错。
从源码看,force_include会通过normalize_inclusion_map把~展开、把相对路径解析为基于项目根的绝对路径(config.py);recurse_forced_files(plugin/interface.py)负责遍历并生成IncludedFile,且强制包含的文件不受 include/exclude/only-packages 过滤。警告:试图覆盖其他文件选择选项已包含的任何文件路径会报错。这一约束在 wheel 归档阶段由_WheelZipFile.open检查实现——同路径二次写入会抛出 ValueError(wheel.py)。
默认文件选择
如果未提供任何文件选择选项,则包含哪些文件由各 构建目标 自行决定。例如 wheel 目标默认会查找包目录,sdist 目标默认包含项目根下的文件,详见 wheel 与 sdist 文档。
排除包之外的文件:only-packages
如果想排除不在 Python 包内的非 artifacts 文件,将only-packages设为true:
[tool.hatch.build.targets.wheel] only-packages = true其语义在include_path()的第一行条件中体现:not (self.only_packages and not is_package)——当启用only-packages时,凡不是包(目录内无__init__.py)的文件都会被拒绝(config.py)。包的判定依据recurse_project_files中is_package = "__init__.py" in files(plugin/interface.py)。
路径重写:sources
sources选项可以重写目录的相对路径。例如:
[tool.hatch.build.targets.wheel.sources] "src/foo" = "bar"会将src/foo/file.ext以bar/file.ext分发。
如果要完全移除路径前缀,与其把每个都设为空字符串,不如把sources定义为数组:
[tool.hatch.build.targets.wheel] sources = ["src"]如果要给路径添加前缀,可以使用空字符串键。例如:
[tool.hatch.build.targets.wheel.sources] "" = "foo"会将bar/file.ext以foo/bar/file.ext分发。
源码中sources同时接受数组(移除前缀)和映射(精确重写)两种形式(config.py),并支持映射形式下空键代表根目录。实际分发路径由get_distribution_path计算:命中 source 前缀则替换,未命中则保持原路径(config.py)。
性能:skip-excluded-dirs
默认情况下所有遇到的目录都会被遍历。要跳过被排除的非 artifacts 目录,可设置skip-excluded-dirs为true:
[tool.hatch.build] skip-excluded-dirs = true警告:这可能导致期望的文件没有被打包。例如想包含a/b/c.txt,但 VCS 忽略 了a/b,那么c.txt将不会被看到,因为其父目录不会被进入。此时可以使用force-include选项。源码中directory_is_excluded在启用该选项时会把目录视为已排除而不进入(config.py);注意目录路径尾部必须带/,这样bar/才能正确匹配foo/bar。此外,一些常见的缓存/虚拟环境目录(如__pycache__、.venv、.git、.hatch、.tox、.ruff_cache等)在任何情况下都会被排除,见 constants.py。
可复现构建(Reproducible builds)
默认情况下,只要 构建目标 支持,就会以可复现的方式构建。要禁用,设置reproducible为false:
[tool.hatch.build] reproducible = false启用后,所有构建时间戳都会使用 SOURCE_DATE_EPOCH。reproducible的默认值在 config.py 中为True。
该选项在 wheel 与 sdist 归档中都有落地:wheel 使用统一时间元组写入 ZipInfo,并将文件权限规范化为 644/755(wheel.py、utils.py);sdist 则将 tar 条目的 uid/gid 归零、清空用户名/组名并统一 mtime(sdist.py)。
输出目录(Output directory)
当未向build命令提供输出目录时,默认使用dist目录。可以通过相对或绝对路径更改默认值:
[tool.hatch.build] directory = "<PATH>"源码中默认值为常量DEFAULT_BUILD_DIRECTORY = "dist"(constants.py),normalize_build_directory会把相对路径基于项目根解析为绝对路径(config.py)。此外,HATCH_BUILD_LOCATION环境变量可以覆盖构建命令的输出位置(见下文环境变量表)。
开发模式(Dev mode)
对于 开发模式 的环境安装或 可编辑安装,wheel目标默认会根据 所选文件 决定哪些目录应加入 Python 的搜索路径(即sys.path)。
要覆盖这一自动检测,或同时指示其他构建目标,可以使用dev-mode-dirs选项:
[tool.hatch.build] dev-mode-dirs = ["."]如果不想把整个目录加入 Python 搜索路径,可以启用更精确的dev-mode-exact选项(与dev-mode-dirs互斥):
[tool.hatch.build] dev-mode-exact = true警告:dev-mode-exact机制不被静态分析工具和 IDE 支持(参见 pylance-release#2114),它通过在每个模块文件旁生成桩模块实现精确映射,而非整个目录进入搜索路径。
构建目标(Build targets)
构建目标可由任何 builder 插件 提供,内置目标有 wheel、sdist 和 custom 三种。
目标依赖
可以为每个构建环境指定额外依赖,例如第三方构建器所需的插件:
[tool.hatch.build.targets.your-target-name] dependencies = [ "your-builder-plugin" ]还可以通过require-runtime-dependencies声明依赖项目的 运行时依赖:
[tool.hatch.build.targets.your-target-name] require-runtime-dependencies = true此外,还可以通过require-runtime-features声明依赖项目的特定 运行时特性:
[tool.hatch.build.targets.your-target-name] require-runtime-features = [ "feature1", "feature2", ]从源码看,dependencies是一个有序去重集合,会合并目标级与全局级依赖、构建钩子的依赖、require-runtime-dependencies展开的项目dependencies、以及require-runtime-features展开的optional-dependencies(config.py)。require-runtime-features中引用的特性必须真实存在于project.optional-dependencies中,否则抛 ValueError(config.py)。
版本(Versions)
如果构建目标支持多种构建策略,或随时间有大版本变更,可以用versions选项指定精确要构建的版本:
[tool.hatch.build.targets.<TARGET_NAME>] versions = [ "v1", "beta-feature", ]参见 wheel 目标的真实示例(standard与editable两种 wheel 构建策略)。源码中versions为空时会回退到构建器声明的默认版本get_default_versions(),且声明的版本必须是get_version_api()提供的版本之一,否则报错(config.py)。实际构建流程在BuilderInterface.build中按版本逐一执行:初始化钩子 → 构建产物 → 收尾钩子,详见 plugin/interface.py。
构建钩子(Build hooks)
构建钩子定义了在构建过程各阶段执行的代码,可由任何 build hook 插件 提供。内置的构建钩子是 custom,它通过加载项目中的构建脚本(默认build.py)来执行自定义逻辑(custom.py)。
构建钩子既可以全局应用:
[tool.hatch.build.hooks.<HOOK_NAME>]也可以应用到特定构建目标:
[tool.hatch.build.targets.<TARGET_NAME>.hooks.<HOOK_NAME>]钩子依赖
可以指定每个构建环境中额外安装的依赖,例如第三方构建钩子:
[tool.hatch.build.hooks.your-hook-name] dependencies = [ "your-build-hook-plugin" ]也可以声明依赖项目的 运行时依赖:
[tool.hatch.build.hooks.your-hook-name] require-runtime-dependencies = true还可以声明依赖项目的特定 运行时特性:
[tool.hatch.build.hooks.your-hook-name] require-runtime-features = [ "feature1", "feature2", ]这些配置在dependencies属性中被统一合并进构建环境的依赖集合(config.py),运行时特性同样必须存在于optional-dependencies。
执行顺序
对每个构建目标,构建钩子按定义顺序执行,全局钩子先执行。例如对于以下配置:
[tool.hatch.build.targets.foo.hooks.hook2] [tool.hatch.build.hooks.hook3] [tool.hatch.build.hooks.hook1]当构建目标foo时,hook3首先执行,然后是hook1,最后是hook2。源码中hook_config先收集全局钩子再收集目标钩子,目标级钩子覆盖同名全局钩子,且键的顺序被保留(config.py);get_build_hooks按此顺序实例化钩子(plugin/interface.py)。
条件执行
如果希望默认禁用某个构建钩子、仅由 环境变量 控制其启用,可以设置enable-by-default为false:
[tool.hatch.build.hooks.<HOOK_NAME>] enable-by-default = false源码中hook_config在以下任一条件满足时才会保留钩子:HATCH_BUILD_HOOKS_ENABLE生效(全部启用)、钩子未显式关闭enable-by-default、或对应的HATCH_BUILD_HOOK_ENABLE_<HOOK_NAME>环境变量为真;而HATCH_BUILD_NO_HOOKS会直接清空全部钩子(config.py)。
环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
HATCH_BUILD_CLEAN | false | 是否先移除已存在的产物 |
HATCH_BUILD_CLEAN_HOOKS_AFTER | false | 每次构建后是否移除构建钩子的产物 |
HATCH_BUILD_HOOKS_ONLY | false | 是否只执行构建钩子 |
HATCH_BUILD_NO_HOOKS | false | 是否禁用所有构建钩子;优先于其他选项 |
HATCH_BUILD_HOOKS_ENABLE | false | 是否启用所有构建钩子 |
HATCH_BUILD_HOOK_ENABLE_<HOOK_NAME> | false | 是否启用名为<HOOK_NAME>的构建钩子 |
HATCH_BUILD_LOCATION | dist | 构建目标的位置;仅由build命令使用 |
这些环境变量在 constants.py 中被定义为BuildEnvVars常量,由env_var_enabled解析——环境变量值只有1或true才视为启用(config.py)。在BuilderInterface.build中,HATCH_BUILD_LOCATION会覆盖输出目录、HATCH_BUILD_CLEAN控制是否清理、HATCH_BUILD_HOOKS_ONLY让流程只跑钩子不产包(plugin/interface.py)。
结语
Hatch 的构建配置是一套层层递进的文件选择与产物生成体系:VCS 规则提供默认基线,include/exclude/artifacts提供模式化筛选,only-include/packages/force-include/sources提供路径级精确控制,only-packages、skip-excluded-dirs提供语义约束与性能调优,而构建目标、构建钩子与环境变量则共同构成了可组合、可复现、可扩展的完整构建流水线。结合 config.py 的源码阅读,可以清楚看到每个配置项背后真实的判定逻辑与优先级,帮助你在实际项目中写出既精确又高效的构建配置。
- 开发工具
- 构建工具
【免费下载链接】hatch
Modern, extensible Python project management
相关推荐
Hatch 源码分发(sdist)构建器完全指南:配置、文件选择与可复现构建
Hatch 源码分发(sdist)构建器完全指南:配置、文件选择与可复现构建 本文以 Hatch 内置的 sdist 构建目标为核心,系统讲解源码分发(Sour
开发工具构建工具Hatch 构建(Builds)完全指南:从 build 配置、构建命令到打包生态兼容
Hatch 构建(Builds)完全指南:从 build 配置、构建命令到打包生态兼容 本篇指南以 Hatch 项目的 docs/build.md 与 docs
开发工具构建工具GoReleaser Go 构建器完全指南:builds 配置、目标矩阵与可复现构建
GoReleaser Go 构建器完全指南:builds 配置、目标矩阵与可复现构建 GoReleaser 的默认构建器(builder)是 Go,它负责把 G
开发工具CI/CD构建工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考