案例:使用 Hatch 完整工作流
案例:使用 Hatch 完整工作流
本案例展示如何使用 Hatch 這個 PyPA 推薦的現代 Python 專案管理工具,完成從專案建立到發布的完整流程。
先備知識
- 6.2 建構系統比較
- Python 虛擬環境基礎
- 基本的命令列操作
問題背景
Hatch 是什麼?
Hatch 是由 PyPA(Python Packaging Authority)成員開發維護的現代 Python 專案管理工具,整合了:
1Hatch 功能整合:
2├── 專案腳手架(hatch new)
3├── 環境管理(類似 tox + virtualenv)
4├── 版本管理(自動更新版本號)
5├── 建構系統(hatchling)
6└── 發布工具(hatch publish)為什麼選擇 Hatch?
| 優勢 | 說明 |
|---|---|
| 標準優先 | 完全遵循 PEP 517/518/621 標準 |
| 一站式工具 | 不需要額外安裝 tox、virtualenv、bump2version |
| 快速建構 | hatchling 建構速度優於 setuptools |
| 環境矩陣 | 內建多 Python 版本測試支援 |
| 腳本系統 | 定義可重用的專案腳本 |
完整工作流
第一步:安裝 Hatch
1# 使用 pip 安裝
2pip install hatch
3
4# 或使用 pipx(推薦,隔離安裝)
5pipx install hatch
6
7# 驗證安裝
8hatch --version第二步:建立新專案
1# 建立新專案
2hatch new my-awesome-lib
3
4# 互動式建立(可自訂選項)
5hatch new my-awesome-lib --init
6
7# 建立應用程式專案(非函式庫)
8hatch new --cli my-cli-app預設專案結構
1my-awesome-lib/
2├── src/
3│ └── my_awesome_lib/
4│ ├── __init__.py
5│ └── __about__.py # 版本資訊
6├── tests/
7│ └── __init__.py
8├── pyproject.toml
9├── README.md
10└── LICENSE.txt生成的 pyproject.toml
1[build-system]
2requires = ["hatchling"]
3build-backend = "hatchling.build"
4
5[project]
6name = "my-awesome-lib"
7dynamic = ["version"]
8description = ''
9readme = "README.md"
10requires-python = ">=3.8"
11license = "MIT"
12keywords = []
13authors = [
14 { name = "Your Name", email = "you@example.com" },
15]
16classifiers = [
17 "Development Status :: 4 - Beta",
18 "Programming Language :: Python",
19 "Programming Language :: Python :: 3.8",
20 "Programming Language :: Python :: 3.9",
21 "Programming Language :: Python :: 3.10",
22 "Programming Language :: Python :: 3.11",
23 "Programming Language :: Python :: 3.12",
24]
25dependencies = []
26
27[project.urls]
28Documentation = "https://github.com/yourname/my-awesome-lib#readme"
29Issues = "https://github.com/yourname/my-awesome-lib/issues"
30Source = "https://github.com/yourname/my-awesome-lib"
31
32[tool.hatch.version]
33path = "src/my_awesome_lib/__about__.py"第三步:環境管理(hatch env)
定義環境
1# pyproject.toml
2
3# 預設環境
4[tool.hatch.envs.default]
5dependencies = [
6 "pytest",
7 "pytest-cov",
8]
9
10[tool.hatch.envs.default.scripts]
11test = "pytest {args:tests}"
12test-cov = "pytest --cov=my_awesome_lib --cov-report=term-missing {args:tests}"
13
14# Lint 環境
15[tool.hatch.envs.lint]
16dependencies = [
17 "ruff>=0.4",
18 "mypy>=1.0",
19]
20
21[tool.hatch.envs.lint.scripts]
22check = [
23 "ruff check src tests",
24 "ruff format --check src tests",
25]
26fix = [
27 "ruff check --fix src tests",
28 "ruff format src tests",
29]
30typing = "mypy src"
31all = ["check", "typing"]
32
33# 文件環境
34[tool.hatch.envs.docs]
35dependencies = [
36 "mkdocs>=1.5",
37 "mkdocs-material>=9.0",
38]
39
40[tool.hatch.envs.docs.scripts]
41build = "mkdocs build"
42serve = "mkdocs serve"使用環境
1# 顯示所有環境
2hatch env show
3
4# 執行預設環境的腳本
5hatch run test
6hatch run test-cov
7
8# 執行特定環境的腳本
9hatch run lint:check
10hatch run lint:fix
11hatch run lint:typing
12
13# 進入環境 shell
14hatch shell
15
16# 進入特定環境
17hatch shell lint
18
19# 移除環境
20hatch env remove
21hatch env remove lint
22
23# 清除所有環境
24hatch env prune第四步:版本管理(hatch version)
設定版本來源
方法 A:從檔案讀取版本
1[tool.hatch.version]
2path = "src/my_awesome_lib/__about__.py"1# src/my_awesome_lib/__about__.py
2__version__ = "0.1.0"方法 B:從 Git 標籤讀取版本(推薦用於開源專案)
1[build-system]
2requires = ["hatchling", "hatch-vcs"]
3build-backend = "hatchling.build"
4
5[tool.hatch.version]
6source = "vcs"
7
8[tool.hatch.build.hooks.vcs]
9version-file = "src/my_awesome_lib/_version.py"版本操作
1# 顯示當前版本
2hatch version
3
4# 設定特定版本
5hatch version 1.0.0
6
7# 語意化版本升級
8hatch version patch # 0.1.0 → 0.1.1
9hatch version minor # 0.1.1 → 0.2.0
10hatch version major # 0.2.0 → 1.0.0
11
12# 預發布版本
13hatch version alpha # 1.0.0 → 1.0.1a0
14hatch version beta # 1.0.1a0 → 1.0.1b0
15hatch version rc # 1.0.1b0 → 1.0.1rc0
16hatch version release # 1.0.1rc0 → 1.0.1
17
18# 開發版本
19hatch version dev # 1.0.0 → 1.0.1.dev0第五步:建構與發布
建構套件
1# 建構 wheel 和 sdist
2hatch build
3
4# 只建構 wheel
5hatch build --target wheel
6
7# 只建構 sdist
8hatch build --target sdist
9
10# 清除建構產物後重建
11hatch build --clean建構產物
1dist/
2├── my_awesome_lib-0.1.0-py3-none-any.whl
3└── my_awesome_lib-0.1.0.tar.gz發布套件
1# 發布到 PyPI(需要設定認證)
2hatch publish
3
4# 發布到 TestPyPI
5hatch publish --repo test
6
7# 指定發布的檔案
8hatch publish dist/my_awesome_lib-0.1.0-py3-none-any.whl設定 PyPI 認證
1# 設定 PyPI token
2hatch config set pypi.auth.username __token__
3hatch config set pypi.auth.password pypi-xxxxx
4
5# 或使用環境變數
6export HATCH_INDEX_USER=__token__
7export HATCH_INDEX_AUTH=pypi-xxxxxpyproject.toml 的 Hatch 特定設定
[tool.hatch.build] 建構設定
1[tool.hatch.build]
2# 包含的檔案(支援 glob)
3include = [
4 "src/my_awesome_lib",
5 "README.md",
6]
7
8# 排除的檔案
9exclude = [
10 "*.pyc",
11 "__pycache__",
12 ".git",
13]
14
15# 是否可重現建構
16reproducible = true
17
18# 開發模式設定
19dev-mode-dirs = ["src"]
20
21[tool.hatch.build.targets.sdist]
22# 原始碼發布設定
23include = [
24 "/src",
25 "/tests",
26 "/README.md",
27 "/LICENSE.txt",
28]
29
30[tool.hatch.build.targets.wheel]
31# Wheel 發布設定
32packages = ["src/my_awesome_lib"]
33
34# 只包含特定平台
35# only-include = ["my_awesome_lib"][tool.hatch.envs] 環境設定
1[tool.hatch.envs.default]
2# 相依性
3dependencies = ["pytest"]
4
5# 額外安裝的 features
6features = ["yaml"]
7
8# 環境變數
9[tool.hatch.envs.default.env-vars]
10PYTHONPATH = "src"
11LOG_LEVEL = "DEBUG"
12
13# 腳本定義
14[tool.hatch.envs.default.scripts]
15test = "pytest {args}"
16
17# 平台特定設定
18[tool.hatch.envs.default.overrides]
19platform.windows.scripts = [
20 'test = "pytest --no-header {args}"',
21][tool.hatch.version] 版本設定
1# 從檔案讀取
2[tool.hatch.version]
3path = "src/my_awesome_lib/__about__.py"
4pattern = "^__version__ = ['\"](/python-advanced/07-packaging/case-studies/hatch-workflow/?P<version>[^'\"]+)['\"]"
5
6# 從 VCS 讀取
7[tool.hatch.version]
8source = "vcs"
9raw-options = { local_scheme = "no-local-version" }
10
11[tool.hatch.build.hooks.vcs]
12version-file = "src/my_awesome_lib/_version.py"[tool.hatch.metadata] 元資料設定
1[tool.hatch.metadata]
2# 允許直接依賴(通常應該避免)
3allow-direct-references = false
4
5# 動態讀取 README
6[tool.hatch.metadata.hooks.fancy-pypi-readme]
7content-type = "text/markdown"
8
9[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
10path = "README.md"與 Poetry 的比較
設計理念差異
1Hatch:
2├── 遵循 PEP 標準優先
3├── 環境管理內建
4├── 不提供依賴鎖定
5├── 建構系統(hatchling)可獨立使用
6└── 設定完全在 [tool.hatch]
7
8Poetry:
9├── 自有生態系統
10├── 強調依賴鎖定(poetry.lock)
11├── 虛擬環境管理
12├── poetry.core 可獨立使用
13└── 混合 [project] 和 [tool.poetry]功能對照
| 功能 | Hatch | Poetry |
|---|---|---|
| 依賴鎖定 | 不支援 | poetry.lock |
| 環境管理 | 內建矩陣支援 | 單一環境 |
| PEP 621 | 完全支援 | Poetry 2.0 支援 |
| 腳本系統 | 強大(環境分離) | 基本 |
| 版本管理 | 內建 bump | 需外掛或手動 |
| 插件系統 | 支援 | 支援 |
pyproject.toml 比較
Hatch 風格
1[build-system]
2requires = ["hatchling"]
3build-backend = "hatchling.build"
4
5[project]
6name = "my-package"
7version = "1.0.0"
8dependencies = ["requests>=2.28"]
9
10[project.optional-dependencies]
11dev = ["pytest>=8.0"]
12
13[tool.hatch.envs.default]
14features = ["dev"]Poetry 風格(2.0)
1[build-system]
2requires = ["poetry-core>=2.0"]
3build-backend = "poetry.core.masonry.api"
4
5[project]
6name = "my-package"
7version = "1.0.0"
8dependencies = ["requests>=2.28"]
9
10[tool.poetry.group.dev.dependencies]
11pytest = "^8.0"選擇建議
1選擇 Hatch:
2├── 開發 Python 函式庫
3├── 需要多環境測試
4├── 偏好標準優先
5└── 不需要依賴鎖定
6
7選擇 Poetry:
8├── 開發應用程式
9├── 需要嚴格的依賴鎖定
10├── 團隊習慣 Poetry 工作流
11└── 需要與現有 Poetry 專案整合實用技巧
多環境測試矩陣
1# 定義多 Python 版本測試
2[tool.hatch.envs.test]
3dependencies = ["pytest", "pytest-cov"]
4
5[[tool.hatch.envs.test.matrix]]
6python = ["3.9", "3.10", "3.11", "3.12", "3.13"]
7
8[tool.hatch.envs.test.scripts]
9run = "pytest {args:tests}"
10cov = "pytest --cov=my_awesome_lib {args:tests}"1# 在所有矩陣環境執行測試
2hatch run test:run
3
4# 在特定版本執行
5hatch run +py=3.12 test:run
6
7# 顯示矩陣環境
8hatch env show --ascii複合腳本
1[tool.hatch.envs.default.scripts]
2# 單一命令
3test = "pytest {args:tests}"
4
5# 多命令(依序執行)
6ci = [
7 "ruff check src tests",
8 "pytest --cov",
9 "mypy src",
10]
11
12# 呼叫其他腳本
13all = ["lint:check", "test:cov"]
14
15# 帶預設參數
16lint = "ruff check {args:.}"環境繼承
1# 基礎環境
2[tool.hatch.envs.base]
3dependencies = ["pytest"]
4
5# 繼承基礎環境
6[tool.hatch.envs.coverage]
7template = "base"
8dependencies = ["pytest-cov"]
9
10[tool.hatch.envs.coverage.scripts]
11run = "pytest --cov {args:tests}"條件依賴
1[tool.hatch.envs.default]
2dependencies = [
3 "pytest",
4]
5
6# 根據平台新增依賴
7[tool.hatch.envs.default.overrides]
8platform.linux.dependencies = ["pytest-xdist"]
9platform.darwin.dependencies = ["pytest-xdist"]
10
11# 根據 Python 版本
12python.3.8.dependencies = ["typing-extensions"]自訂建構 Hook
1[tool.hatch.build.hooks.custom]
2# 建構前執行
3path = "hatch_build.py"1# hatch_build.py
2from hatchling.builders.hooks.plugin.interface import BuildHookInterface
3
4class CustomBuildHook(BuildHookInterface):
5 def initialize(self, version, build_data):
6 # 建構前的自訂邏輯
7 print(f"Building version {version}")完整範例:CLI 應用程式
1# pyproject.toml
2[build-system]
3requires = ["hatchling"]
4build-backend = "hatchling.build"
5
6[project]
7name = "my-cli"
8dynamic = ["version"]
9description = "A useful CLI tool"
10readme = "README.md"
11requires-python = ">=3.10"
12license = "MIT"
13dependencies = [
14 "click>=8.0",
15 "rich>=13.0",
16]
17
18[project.scripts]
19my-cli = "my_cli.main:app"
20
21[project.optional-dependencies]
22yaml = ["PyYAML>=6.0"]
23
24[tool.hatch.version]
25path = "src/my_cli/__about__.py"
26
27[tool.hatch.build.targets.wheel]
28packages = ["src/my_cli"]
29
30# ===== 環境設定 =====
31
32[tool.hatch.envs.default]
33dependencies = [
34 "pytest>=8.0",
35 "pytest-cov>=4.0",
36]
37
38[tool.hatch.envs.default.scripts]
39test = "pytest {args:tests}"
40cov = "pytest --cov=my_cli --cov-report=term-missing {args:tests}"
41
42[tool.hatch.envs.lint]
43dependencies = [
44 "ruff>=0.4",
45 "mypy>=1.0",
46]
47
48[tool.hatch.envs.lint.scripts]
49check = ["ruff check src tests", "ruff format --check src tests"]
50fix = ["ruff check --fix src tests", "ruff format src tests"]
51typing = "mypy src"
52all = ["check", "typing"]
53
54[[tool.hatch.envs.test.matrix]]
55python = ["3.10", "3.11", "3.12", "3.13"]
56
57# ===== 工具設定 =====
58
59[tool.ruff]
60src = ["src"]
61line-length = 88
62
63[tool.ruff.lint]
64select = ["E", "W", "F", "I", "B", "UP"]
65
66[tool.mypy]
67python_version = "3.10"
68strict = true
69
70[tool.pytest.ini_options]
71testpaths = ["tests"]
72addopts = "-v"發布檢查清單
1發布前檢查:
2├── [ ] hatch run lint:all 通過
3├── [ ] hatch run test:run 在所有 Python 版本通過
4├── [ ] hatch version 更新版本號
5├── [ ] 更新 CHANGELOG.md
6├── [ ] hatch build 建構成功
7├── [ ] 在虛擬環境測試安裝:pip install dist/*.whl
8├── [ ] hatch publish --repo test 發布到 TestPyPI
9├── [ ] 從 TestPyPI 測試安裝
10└── [ ] hatch publish 發布到 PyPI延伸閱讀
返回:案例研究