<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Packaging on Tarragon</title><link>https://tarrragon.github.io/blog/tags/packaging/</link><description>Recent content in Packaging on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 02 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/packaging/index.xml" rel="self" type="application/rss+xml"/><item><title>案例：打包共用庫</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/package-library/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/package-library/</guid><description>&lt;p>本案例基於 &lt;code>.claude/lib&lt;/code> 整體結構，展示如何將內部共用庫打包成可重用的 Python 套件。&lt;/p>
&lt;h2 id="先備知識">先備知識&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/" data-link-title="模組七：打包與發布" data-link-desc="學習現代 Python 套件的打包與發布流程">模組六：打包與發布&lt;/a>&lt;/li>
&lt;li>Python 模組與套件基礎&lt;/li>
&lt;/ul>
&lt;h2 id="問題背景">問題背景&lt;/h2>
&lt;h3 id="現有設計">現有設計&lt;/h3>
&lt;p>&lt;code>.claude/lib&lt;/code> 目錄結構：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">.claude/lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── __init__.py # Package entry point with version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── config_loader.py # YAML/JSON configuration loader
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── git_utils.py # Git operations (branch, worktree)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── hook_io.py # Hook I/O standardization
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── hook_logging.py # Logging system for hooks
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── hook_validator.py # Hook compliance validator
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── markdown_link_checker.py # Markdown link validation
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── README.md # API documentation
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">└── tests/ # Unit tests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> ├── test_config_loader.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> ├── test_git_utils.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> ├── test_hook_io.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> └── test_hook_logging.py&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這是一個典型的內部工具庫，包含四個核心模組：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>模組&lt;/th>
 &lt;th>功能&lt;/th>
 &lt;th>相依性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>config_loader&lt;/code>&lt;/td>
 &lt;td>YAML/JSON 配置載入&lt;/td>
 &lt;td>PyYAML (optional)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>git_utils&lt;/code>&lt;/td>
 &lt;td>Git 命令執行與分支管理&lt;/td>
 &lt;td>無（使用 subprocess）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>hook_io&lt;/code>&lt;/td>
 &lt;td>Hook 輸入輸出標準化&lt;/td>
 &lt;td>無（使用標準庫）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>hook_logging&lt;/code>&lt;/td>
 &lt;td>日誌系統設定&lt;/td>
 &lt;td>無（使用標準庫）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="現有限制">現有限制&lt;/h3>
&lt;p>作為內部目錄的問題：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>無法跨專案重用&lt;/strong>：程式碼被鎖定在單一專案中&lt;/li>
&lt;li>&lt;strong>沒有版本管理&lt;/strong>：無法追蹤 API 變更&lt;/li>
&lt;li>&lt;strong>無法透過 pip 安裝&lt;/strong>：其他專案必須複製程式碼&lt;/li>
&lt;li>&lt;strong>相依性管理不明確&lt;/strong>：PyYAML 是可選還是必要？&lt;/li>
&lt;/ul>
&lt;h2 id="進階解決方案">進階解決方案&lt;/h2>
&lt;h3 id="設計目標">設計目標&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>建立標準的 Python 套件結構&lt;/strong>&lt;/li>
&lt;li>&lt;strong>使用 pyproject.toml 管理元資料&lt;/strong>&lt;/li>
&lt;li>&lt;strong>支援 pip install&lt;/strong>&lt;/li>
&lt;li>&lt;strong>建立 CI/CD 發布流程&lt;/strong>&lt;/li>
&lt;/ol>
&lt;h3 id="實作步驟">實作步驟&lt;/h3>
&lt;h4 id="步驟-1重組目錄結構">步驟 1：重組目錄結構&lt;/h4>
&lt;p>從內部目錄結構轉換為標準的 &lt;strong>src layout&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">claude-hooks-lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── pyproject.toml # Package metadata and build config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── README.md # Package documentation
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── LICENSE # License file (MIT recommended)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── CHANGELOG.md # Version history
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ └── claude_hooks_lib/ # Package directory (underscore for import)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">│ ├── __init__.py # Public API exports
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ ├── config_loader.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">│ ├── git_utils.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│ ├── hook_io.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">│ ├── hook_logging.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">│ ├── hook_validator.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">│ └── py.typed # PEP 561 marker for type hints
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> ├── conftest.py # Pytest fixtures
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> ├── test_config_loader.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> ├── test_git_utils.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> ├── test_hook_io.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> └── test_hook_logging.py&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="為什麼選擇-src-layout">為什麼選擇 src layout？&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl"># Flat layout (不推薦用於套件發布)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">my-package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── my_package/ # Package 直接在根目錄
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">│ └── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"># Src layout (推薦)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">my-package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">│ └── my_package/ # Package 在 src/ 下
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│ └── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">└── tests/&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>特性&lt;/th>
 &lt;th>Flat Layout&lt;/th>
 &lt;th>Src Layout&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>測試環境&lt;/td>
 &lt;td>可能意外導入本地版本&lt;/td>
 &lt;td>強制安裝後測試&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>套件發布&lt;/td>
 &lt;td>容易遺漏檔案&lt;/td>
 &lt;td>明確的套件邊界&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>複雜度&lt;/td>
 &lt;td>較低&lt;/td>
 &lt;td>稍高&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>推薦場景&lt;/td>
 &lt;td>簡單專案、應用程式&lt;/td>
 &lt;td>套件發布、函式庫&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h4 id="步驟-2建立-pyprojecttoml">步驟 2：建立 pyproject.toml&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;hatchling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;hatchling.build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;claude-hooks-lib&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.28.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Shared utilities for Claude Code hooks&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;MIT&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;your.email@example.com&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">keywords&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;claude&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;hooks&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;utilities&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;git&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">classifiers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Development Status :: 4 - Beta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Intended Audience :: Developers&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;License :: OSI Approved :: MIT License&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Operating System :: OS Independent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.11&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.13&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Typing :: Typed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="c"># Core dependencies (minimal)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="c"># YAML support&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="nx">yaml&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;PyYAML&amp;gt;=6.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="c"># Development dependencies&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest-cov&amp;gt;=4.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mypy&amp;gt;=1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff&amp;gt;=0.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl">&lt;span class="c"># All optional features&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl">&lt;span class="nx">all&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;claude-hooks-lib[yaml]&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">urls&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl">&lt;span class="nx">Homepage&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/claude-hooks-lib&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl">&lt;span class="nx">Documentation&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/claude-hooks-lib#readme&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl">&lt;span class="nx">Repository&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/claude-hooks-lib.git&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl">&lt;span class="nx">Changelog&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/claude-hooks-lib/blob/main/CHANGELOG.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl">&lt;span class="c"># Command-line entry points&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl">&lt;span class="nx">hook-validator&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;claude_hooks_lib.hook_validator:main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">targets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sdist&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl">&lt;span class="nx">include&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;/src&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;/tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">targets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">wheel&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src/claude_hooks_lib&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="關鍵設定說明">關鍵設定說明&lt;/h5>
&lt;ol>
&lt;li>&lt;strong>build-system&lt;/strong>：使用 Hatch 作為建構後端（現代、快速）&lt;/li>
&lt;li>&lt;strong>requires-python&lt;/strong>：指定最低 Python 版本&lt;/li>
&lt;li>&lt;strong>dependencies&lt;/strong>：核心相依性保持為空（僅使用標準庫）&lt;/li>
&lt;li>&lt;strong>optional-dependencies&lt;/strong>：將 PyYAML 設為可選&lt;/li>
&lt;li>&lt;strong>project.scripts&lt;/strong>：定義命令列工具入口點&lt;/li>
&lt;/ol>
&lt;h4 id="步驟-3處理相依性">步驟 3：處理相依性&lt;/h4>
&lt;p>相依性分層策略：&lt;/p></description><content:encoded><![CDATA[<p>本案例基於 <code>.claude/lib</code> 整體結構，展示如何將內部共用庫打包成可重用的 Python 套件。</p>
<h2 id="先備知識">先備知識</h2>
<ul>
<li><a href="/blog/python-advanced/07-packaging/" data-link-title="模組七：打包與發布" data-link-desc="學習現代 Python 套件的打包與發布流程">模組六：打包與發布</a></li>
<li>Python 模組與套件基礎</li>
</ul>
<h2 id="問題背景">問題背景</h2>
<h3 id="現有設計">現有設計</h3>
<p><code>.claude/lib</code> 目錄結構：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">.claude/lib/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── __init__.py              # Package entry point with version
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── config_loader.py         # YAML/JSON configuration loader
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── git_utils.py             # Git operations (branch, worktree)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── hook_io.py               # Hook I/O standardization
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── hook_logging.py          # Logging system for hooks
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── hook_validator.py        # Hook compliance validator
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── markdown_link_checker.py # Markdown link validation
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── README.md                # API documentation
</span></span><span class="line"><span class="ln">10</span><span class="cl">└── tests/                   # Unit tests
</span></span><span class="line"><span class="ln">11</span><span class="cl">    ├── __init__.py
</span></span><span class="line"><span class="ln">12</span><span class="cl">    ├── test_config_loader.py
</span></span><span class="line"><span class="ln">13</span><span class="cl">    ├── test_git_utils.py
</span></span><span class="line"><span class="ln">14</span><span class="cl">    ├── test_hook_io.py
</span></span><span class="line"><span class="ln">15</span><span class="cl">    └── test_hook_logging.py</span></span></code></pre></div><p>這是一個典型的內部工具庫，包含四個核心模組：</p>
<table>
  <thead>
      <tr>
          <th>模組</th>
          <th>功能</th>
          <th>相依性</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>config_loader</code></td>
          <td>YAML/JSON 配置載入</td>
          <td>PyYAML (optional)</td>
      </tr>
      <tr>
          <td><code>git_utils</code></td>
          <td>Git 命令執行與分支管理</td>
          <td>無（使用 subprocess）</td>
      </tr>
      <tr>
          <td><code>hook_io</code></td>
          <td>Hook 輸入輸出標準化</td>
          <td>無（使用標準庫）</td>
      </tr>
      <tr>
          <td><code>hook_logging</code></td>
          <td>日誌系統設定</td>
          <td>無（使用標準庫）</td>
      </tr>
  </tbody>
</table>
<h3 id="現有限制">現有限制</h3>
<p>作為內部目錄的問題：</p>
<ul>
<li><strong>無法跨專案重用</strong>：程式碼被鎖定在單一專案中</li>
<li><strong>沒有版本管理</strong>：無法追蹤 API 變更</li>
<li><strong>無法透過 pip 安裝</strong>：其他專案必須複製程式碼</li>
<li><strong>相依性管理不明確</strong>：PyYAML 是可選還是必要？</li>
</ul>
<h2 id="進階解決方案">進階解決方案</h2>
<h3 id="設計目標">設計目標</h3>
<ol>
<li><strong>建立標準的 Python 套件結構</strong></li>
<li><strong>使用 pyproject.toml 管理元資料</strong></li>
<li><strong>支援 pip install</strong></li>
<li><strong>建立 CI/CD 發布流程</strong></li>
</ol>
<h3 id="實作步驟">實作步驟</h3>
<h4 id="步驟-1重組目錄結構">步驟 1：重組目錄結構</h4>
<p>從內部目錄結構轉換為標準的 <strong>src layout</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">claude-hooks-lib/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── pyproject.toml           # Package metadata and build config
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── README.md                # Package documentation
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── LICENSE                  # License file (MIT recommended)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── CHANGELOG.md             # Version history
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│   └── claude_hooks_lib/    # Package directory (underscore for import)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">│       ├── __init__.py      # Public API exports
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│       ├── config_loader.py
</span></span><span class="line"><span class="ln">10</span><span class="cl">│       ├── git_utils.py
</span></span><span class="line"><span class="ln">11</span><span class="cl">│       ├── hook_io.py
</span></span><span class="line"><span class="ln">12</span><span class="cl">│       ├── hook_logging.py
</span></span><span class="line"><span class="ln">13</span><span class="cl">│       ├── hook_validator.py
</span></span><span class="line"><span class="ln">14</span><span class="cl">│       └── py.typed         # PEP 561 marker for type hints
</span></span><span class="line"><span class="ln">15</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln">16</span><span class="cl">    ├── __init__.py
</span></span><span class="line"><span class="ln">17</span><span class="cl">    ├── conftest.py          # Pytest fixtures
</span></span><span class="line"><span class="ln">18</span><span class="cl">    ├── test_config_loader.py
</span></span><span class="line"><span class="ln">19</span><span class="cl">    ├── test_git_utils.py
</span></span><span class="line"><span class="ln">20</span><span class="cl">    ├── test_hook_io.py
</span></span><span class="line"><span class="ln">21</span><span class="cl">    └── test_hook_logging.py</span></span></code></pre></div><h5 id="為什麼選擇-src-layout">為什麼選擇 src layout？</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl"># Flat layout (不推薦用於套件發布)
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">my-package/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── my_package/          # Package 直接在根目錄
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">│   └── __init__.py
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"># Src layout (推薦)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">my-package/
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln">10</span><span class="cl">│   └── my_package/      # Package 在 src/ 下
</span></span><span class="line"><span class="ln">11</span><span class="cl">│       └── __init__.py
</span></span><span class="line"><span class="ln">12</span><span class="cl">└── tests/</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>特性</th>
          <th>Flat Layout</th>
          <th>Src Layout</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>測試環境</td>
          <td>可能意外導入本地版本</td>
          <td>強制安裝後測試</td>
      </tr>
      <tr>
          <td>套件發布</td>
          <td>容易遺漏檔案</td>
          <td>明確的套件邊界</td>
      </tr>
      <tr>
          <td>複雜度</td>
          <td>較低</td>
          <td>稍高</td>
      </tr>
      <tr>
          <td>推薦場景</td>
          <td>簡單專案、應用程式</td>
          <td>套件發布、函式庫</td>
      </tr>
  </tbody>
</table>
<h4 id="步驟-2建立-pyprojecttoml">步驟 2：建立 pyproject.toml</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;claude-hooks-lib&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.28.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Shared utilities for Claude Code hooks&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.10&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">{</span> <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;your.email@example.com&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">keywords</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;claude&#34;</span><span class="p">,</span> <span class="s2">&#34;hooks&#34;</span><span class="p">,</span> <span class="s2">&#34;utilities&#34;</span><span class="p">,</span> <span class="s2">&#34;git&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">classifiers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="s2">&#34;Development Status :: 4 - Beta&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="s2">&#34;Intended Audience :: Developers&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="s2">&#34;License :: OSI Approved :: MIT License&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="s2">&#34;Operating System :: OS Independent&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.11&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.12&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.13&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="s2">&#34;Typing :: Typed&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c"># Core dependencies (minimal)</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="c"># YAML support</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="nx">yaml</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;PyYAML&gt;=6.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c"># Development dependencies</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="s2">&#34;pytest&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="s2">&#34;pytest-cov&gt;=4.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="s2">&#34;mypy&gt;=1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="s2">&#34;ruff&gt;=0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="c"># All optional features</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;claude-hooks-lib[yaml]&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">urls</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="nx">Homepage</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib&#34;</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="nx">Documentation</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib#readme&#34;</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="nx">Repository</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib.git&#34;</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="nx">Changelog</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib/blob/main/CHANGELOG.md&#34;</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="c"># Command-line entry points</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="nx">hook-validator</span> <span class="p">=</span> <span class="s2">&#34;claude_hooks_lib.hook_validator:main&#34;</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">sdist</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="nx">include</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">    <span class="s2">&#34;/src&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">    <span class="s2">&#34;/tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/claude_hooks_lib&#34;</span><span class="p">]</span></span></span></code></pre></div><h5 id="關鍵設定說明">關鍵設定說明</h5>
<ol>
<li><strong>build-system</strong>：使用 Hatch 作為建構後端（現代、快速）</li>
<li><strong>requires-python</strong>：指定最低 Python 版本</li>
<li><strong>dependencies</strong>：核心相依性保持為空（僅使用標準庫）</li>
<li><strong>optional-dependencies</strong>：將 PyYAML 設為可選</li>
<li><strong>project.scripts</strong>：定義命令列工具入口點</li>
</ol>
<h4 id="步驟-3處理相依性">步驟 3：處理相依性</h4>
<p>相依性分層策略：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># 核心相依性：僅標準庫</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c"># 功能性相依性（用戶根據需求安裝）</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">yaml</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;PyYAML&gt;=6.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c"># 開發相依性（僅開發者需要）</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s2">&#34;pytest&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="s2">&#34;pytest-cov&gt;=4.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="s2">&#34;mypy&gt;=1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;ruff&gt;=0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c"># 測試相依性（CI/CD 需要）</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nx">test</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="s2">&#34;pytest&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="s2">&#34;pytest-cov&gt;=4.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c"># 文件相依性</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="nx">docs</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="s2">&#34;mkdocs&gt;=1.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="s2">&#34;mkdocs-material&gt;=9.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c"># 完整安裝</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="s2">&#34;claude-hooks-lib[yaml,dev,docs]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">]</span></span></span></code></pre></div><h5 id="安裝方式範例">安裝方式範例</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 基本安裝（無可選相依性）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install claude-hooks-lib
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 包含 YAML 支援</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">pip install <span class="s2">&#34;claude-hooks-lib[yaml]&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 開發者安裝</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">pip install -e <span class="s2">&#34;.[dev]&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 完整安裝</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">pip install <span class="s2">&#34;claude-hooks-lib[all]&#34;</span></span></span></code></pre></div><h4 id="步驟-4版本管理策略">步驟 4：版本管理策略</h4>
<h5 id="方法-a單一來源版本推薦">方法 A：單一來源版本（推薦）</h5>
<p>在 <code>__init__.py</code> 中定義版本：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># src/claude_hooks_lib/__init__.py</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="s2">Claude Hooks Library
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="s2">Shared utilities for building Claude Code hooks.
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">__version__</span> <span class="o">=</span> <span class="s2">&#34;0.28.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="c1"># Version</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s2">&#34;__version__&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1"># git_utils</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="s2">&#34;run_git_command&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;get_current_branch&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s2">&#34;get_project_root&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="s2">&#34;get_worktree_list&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="s2">&#34;is_protected_branch&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="s2">&#34;is_allowed_branch&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="c1"># hook_logging</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="s2">&#34;setup_hook_logging&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="c1"># hook_io</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="s2">&#34;read_hook_input&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="s2">&#34;write_hook_output&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">&#34;create_pretooluse_output&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="s2">&#34;create_posttooluse_output&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="c1"># config_loader</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="s2">&#34;load_config&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="s2">&#34;load_agents_config&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="s2">&#34;load_quality_rules&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="s2">&#34;clear_config_cache&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="kn">from</span> <span class="nn">.git_utils</span> <span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="n">run_git_command</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="n">get_current_branch</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="n">get_project_root</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="n">get_worktree_list</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="n">is_protected_branch</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="n">is_allowed_branch</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="kn">from</span> <span class="nn">.hook_logging</span> <span class="kn">import</span> <span class="n">setup_hook_logging</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="kn">from</span> <span class="nn">.hook_io</span> <span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="n">read_hook_input</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="n">write_hook_output</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="n">create_pretooluse_output</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    <span class="n">create_posttooluse_output</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="kn">from</span> <span class="nn">.config_loader</span> <span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="n">load_config</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">    <span class="n">load_agents_config</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">    <span class="n">load_quality_rules</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">    <span class="n">clear_config_cache</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="p">)</span></span></span></code></pre></div><p>在 <code>pyproject.toml</code> 中使用動態版本：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;claude-hooks-lib&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;src/claude_hooks_lib/__init__.py&#34;</span></span></span></code></pre></div><h5 id="方法-b使用-hatch-vcsgit-tag-版本">方法 B：使用 hatch-vcs（Git tag 版本）</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">,</span> <span class="s2">&#34;hatch-vcs&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">source</span> <span class="p">=</span> <span class="s2">&#34;vcs&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">vcs</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">version-file</span> <span class="p">=</span> <span class="s2">&#34;src/claude_hooks_lib/_version.py&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Create version tag</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">git tag v0.28.0
</span></span><span class="line"><span class="ln">3</span><span class="cl">git push --tags</span></span></code></pre></div><h4 id="步驟-5建立發布流程">步驟 5：建立發布流程</h4>
<p><strong>.github/workflows/publish.yml</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish to PyPI</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">release</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">published]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">  </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="l">read</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write </span><span class="w"> </span><span class="c"># Required for trusted publishing</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up Python</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3.12&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install build tools</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="sd">          python -m pip install --upgrade pip
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="sd">          pip install build</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build package</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">python -m build</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload artifacts</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">        </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;3.10&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.11&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.12&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.13&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up Python ${{ matrix.python-version }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.python-version }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install dependencies</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="sd">          python -m pip install --upgrade pip
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="sd">          pip install -e &#34;.[dev,yaml]&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run tests</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pytest tests/ -v --cov=src/claude_hooks_lib</span><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Type check</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">mypy src/claude_hooks_lib</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">  </span><span class="nt">publish-testpypi</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">build, test]</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">testpypi</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Download artifacts</span><span class="w">
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish to TestPyPI</span><span class="w">
</span></span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/gh-action-pypi-publish@release/v1</span><span class="w">
</span></span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="w">          </span><span class="nt">repository-url</span><span class="p">:</span><span class="w"> </span><span class="l">https://test.pypi.org/legacy/</span><span class="w">
</span></span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="w">  </span><span class="nt">publish-pypi</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">publish-testpypi]</span><span class="w">
</span></span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">79</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">pypi</span><span class="w">
</span></span></span><span class="line"><span class="ln">80</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Download artifacts</span><span class="w">
</span></span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">83</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">84</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">85</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">86</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">87</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish to PyPI</span><span class="w">
</span></span></span><span class="line"><span class="ln">88</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/gh-action-pypi-publish@release/v1</span></span></span></code></pre></div><p><strong>CI 測試工作流程（.github/workflows/test.yml）</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Tests</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.os }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span><span class="nt">fail-fast</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="nt">os</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ubuntu-latest, macos-latest, windows-latest]</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;3.10&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.11&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.12&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.13&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up Python ${{ matrix.python-version }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.python-version }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install dependencies</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="sd">          python -m pip install --upgrade pip
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="sd">          pip install -e &#34;.[dev,yaml]&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Lint with ruff</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">ruff check src/ tests/</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Format check with ruff</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">ruff format --check src/ tests/</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Type check with mypy</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">mypy src/claude_hooks_lib</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run tests with coverage</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="sd">          pytest tests/ -v --cov=src/claude_hooks_lib --cov-report=xml</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload coverage</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">codecov/codecov-action@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">          </span><span class="nt">files</span><span class="p">:</span><span class="w"> </span><span class="l">./coverage.xml</span></span></span></code></pre></div><h3 id="完整程式碼">完整程式碼</h3>
<h4 id="完整的-pyprojecttoml">完整的 pyproject.toml</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">  1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln">  4</span><span class="cl">
</span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;claude-hooks-lib&#34;</span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Shared utilities for Claude Code hooks&#34;</span>
</span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span>
</span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.10&#34;</span>
</span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 13</span><span class="cl">    <span class="p">{</span> <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;your.email@example.com&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="nx">keywords</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;claude&#34;</span><span class="p">,</span> <span class="s2">&#34;hooks&#34;</span><span class="p">,</span> <span class="s2">&#34;utilities&#34;</span><span class="p">,</span> <span class="s2">&#34;git&#34;</span><span class="p">,</span> <span class="s2">&#34;automation&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="nx">classifiers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 17</span><span class="cl">    <span class="s2">&#34;Development Status :: 4 - Beta&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">    <span class="s2">&#34;Intended Audience :: Developers&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 19</span><span class="cl">    <span class="s2">&#34;License :: OSI Approved :: MIT License&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 20</span><span class="cl">    <span class="s2">&#34;Operating System :: OS Independent&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 21</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 22</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.11&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.12&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 25</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.13&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 26</span><span class="cl">    <span class="s2">&#34;Typing :: Typed&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 27</span><span class="cl">    <span class="s2">&#34;Topic :: Software Development :: Libraries :: Python Modules&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 29</span><span class="cl">
</span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 31</span><span class="cl">
</span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="nx">yaml</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;PyYAML&gt;=6.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 35</span><span class="cl">    <span class="s2">&#34;pytest&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 36</span><span class="cl">    <span class="s2">&#34;pytest-cov&gt;=4.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 37</span><span class="cl">    <span class="s2">&#34;mypy&gt;=1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 38</span><span class="cl">    <span class="s2">&#34;ruff&gt;=0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 39</span><span class="cl">    <span class="s2">&#34;PyYAML&gt;=6.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="nx">docs</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">    <span class="s2">&#34;mkdocs&gt;=1.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 43</span><span class="cl">    <span class="s2">&#34;mkdocs-material&gt;=9.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;claude-hooks-lib[yaml,dev,docs]&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">
</span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">urls</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="nx">Homepage</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib&#34;</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="nx">Documentation</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib#readme&#34;</span>
</span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="nx">Repository</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib.git&#34;</span>
</span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="nx">Changelog</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/claude-hooks-lib/blob/main/CHANGELOG.md&#34;</span>
</span></span><span class="line"><span class="ln"> 52</span><span class="cl">
</span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="nx">hook-validator</span> <span class="p">=</span> <span class="s2">&#34;claude_hooks_lib.hook_validator:main&#34;</span>
</span></span><span class="line"><span class="ln"> 55</span><span class="cl">
</span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="c"># ===== Build Configuration =====</span>
</span></span><span class="line"><span class="ln"> 57</span><span class="cl">
</span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;src/claude_hooks_lib/__init__.py&#34;</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">
</span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">sdist</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="nx">include</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;/src&#34;</span><span class="p">,</span> <span class="s2">&#34;/tests&#34;</span><span class="p">,</span> <span class="s2">&#34;/README.md&#34;</span><span class="p">,</span> <span class="s2">&#34;/LICENSE&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">
</span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/claude_hooks_lib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">
</span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="c"># ===== Tool Configuration =====</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">
</span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="nx">src</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="nx">line-length</span> <span class="p">=</span> <span class="mi">88</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="nx">target-version</span> <span class="p">=</span> <span class="s2">&#34;py310&#34;</span>
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">
</span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="nx">select</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">    <span class="s2">&#34;E&#34;</span><span class="p">,</span>      <span class="c"># pycodestyle errors</span>
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">    <span class="s2">&#34;W&#34;</span><span class="p">,</span>      <span class="c"># pycodestyle warnings</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">    <span class="s2">&#34;F&#34;</span><span class="p">,</span>      <span class="c"># pyflakes</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">    <span class="s2">&#34;I&#34;</span><span class="p">,</span>      <span class="c"># isort</span>
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">    <span class="s2">&#34;B&#34;</span><span class="p">,</span>      <span class="c"># flake8-bugbear</span>
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">    <span class="s2">&#34;C4&#34;</span><span class="p">,</span>     <span class="c"># flake8-comprehensions</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">    <span class="s2">&#34;UP&#34;</span><span class="p">,</span>     <span class="c"># pyupgrade</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="nx">ignore</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;E501&#34;</span><span class="p">]</span>  <span class="c"># Line too long (handled by formatter)</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">
</span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">.</span><span class="nx">lint</span><span class="p">.</span><span class="nx">isort</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="nx">known-first-party</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;claude_hooks_lib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">
</span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">mypy</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="nx">python_version</span> <span class="p">=</span> <span class="s2">&#34;3.10&#34;</span>
</span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="nx">warn_return_any</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="nx">warn_unused_ignores</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="nx">disallow_untyped_defs</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="nx">strict</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">
</span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="p">[[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">mypy</span><span class="p">.</span><span class="nx">overrides</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="nx">module</span> <span class="p">=</span> <span class="s2">&#34;yaml&#34;</span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="nx">ignore_missing_imports</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">
</span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">pytest</span><span class="p">.</span><span class="nx">ini_options</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="nx">testpaths</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;tests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="nx">python_files</span> <span class="p">=</span> <span class="s2">&#34;test_*.py&#34;</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="nx">python_functions</span> <span class="p">=</span> <span class="s2">&#34;test_*&#34;</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="nx">addopts</span> <span class="p">=</span> <span class="s2">&#34;-v --tb=short&#34;</span>
</span></span><span class="line"><span class="ln">105</span><span class="cl">
</span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">coverage</span><span class="p">.</span><span class="nx">run</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="nx">source</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/claude_hooks_lib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">108</span><span class="cl"><span class="nx">branch</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">109</span><span class="cl">
</span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">coverage</span><span class="p">.</span><span class="nx">report</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">111</span><span class="cl"><span class="nx">exclude_lines</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">112</span><span class="cl">    <span class="s2">&#34;pragma: no cover&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">113</span><span class="cl">    <span class="s2">&#34;if TYPE_CHECKING:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">114</span><span class="cl">    <span class="s2">&#34;raise NotImplementedError&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">115</span><span class="cl"><span class="p">]</span></span></span></code></pre></div><h3 id="使用範例">使用範例</h3>
<h4 id="安裝套件">安裝套件</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># From PyPI (after publishing)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install claude-hooks-lib
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># With YAML support</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">pip install <span class="s2">&#34;claude-hooks-lib[yaml]&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># Development installation (from source)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">git clone https://github.com/yourname/claude-hooks-lib
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nb">cd</span> claude-hooks-lib
</span></span><span class="line"><span class="ln">10</span><span class="cl">pip install -e <span class="s2">&#34;.[dev]&#34;</span></span></span></code></pre></div><h5 id="python-使用範例">Python 使用範例</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Basic usage</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">from</span> <span class="nn">claude_hooks_lib</span> <span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">get_current_branch</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">is_protected_branch</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">setup_hook_logging</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">read_hook_input</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">write_hook_output</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">create_pretooluse_output</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># Initialize logging</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">logger</span> <span class="o">=</span> <span class="n">setup_hook_logging</span><span class="p">(</span><span class="s2">&#34;my-custom-hook&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># Check branch protection</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="n">branch</span> <span class="o">=</span> <span class="n">get_current_branch</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">if</span> <span class="n">branch</span> <span class="ow">and</span> <span class="n">is_protected_branch</span><span class="p">(</span><span class="n">branch</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Operating on protected branch: </span><span class="si">{</span><span class="n">branch</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"># Process hook input</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="n">input_data</span> <span class="o">=</span> <span class="n">read_hook_input</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="n">tool_name</span> <span class="o">=</span> <span class="n">input_data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;tool_name&#34;</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"># Generate output</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="n">output</span> <span class="o">=</span> <span class="n">create_pretooluse_output</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">decision</span><span class="o">=</span><span class="s2">&#34;allow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">reason</span><span class="o">=</span><span class="s2">&#34;All checks passed&#34;</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="n">write_hook_output</span><span class="p">(</span><span class="n">output</span><span class="p">)</span></span></span></code></pre></div><h4 id="命令列工具使用">命令列工具使用</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Validate a single hook</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">hook-validator .claude/hooks/my-hook.py
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># Validate all hooks</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">hook-validator --all
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># Output as JSON</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">hook-validator --all --json
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># Strict mode (warnings as errors)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">hook-validator --all --strict</span></span></code></pre></div><h2 id="設計權衡">設計權衡</h2>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>內部目錄</th>
          <th>獨立套件</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>重用性</strong></td>
          <td>僅限單專案</td>
          <td>跨專案共用</td>
      </tr>
      <tr>
          <td><strong>版本管理</strong></td>
          <td>與專案綁定</td>
          <td>獨立語意化版本</td>
      </tr>
      <tr>
          <td><strong>維護成本</strong></td>
          <td>低（無發布流程）</td>
          <td>中（需維護 CI/CD）</td>
      </tr>
      <tr>
          <td><strong>相依管理</strong></td>
          <td>隱式（需手動追蹤）</td>
          <td>顯式（pyproject.toml）</td>
      </tr>
      <tr>
          <td><strong>安裝方式</strong></td>
          <td>複製程式碼或 sys.path</td>
          <td>pip install</td>
      </tr>
      <tr>
          <td><strong>測試隔離</strong></td>
          <td>可能測試到本地版本</td>
          <td>強制測試安裝版本</td>
      </tr>
      <tr>
          <td><strong>API 穩定性</strong></td>
          <td>無保證</td>
          <td>版本號約束</td>
      </tr>
  </tbody>
</table>
<h3 id="專案結構比較">專案結構比較</h3>
<table>
  <thead>
      <tr>
          <th>Layout</th>
          <th>適用場景</th>
          <th>優點</th>
          <th>缺點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Flat Layout</strong></td>
          <td>簡單應用、腳本</td>
          <td>簡單直覺</td>
          <td>測試可能導入錯誤版本</td>
      </tr>
      <tr>
          <td><strong>Src Layout</strong></td>
          <td>函式庫、套件發布</td>
          <td>測試隔離、明確邊界</td>
          <td>額外一層目錄</td>
      </tr>
  </tbody>
</table>
<h3 id="建構工具比較">建構工具比較</h3>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>特點</th>
          <th>推薦場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>setuptools</strong></td>
          <td>成熟穩定、生態最大</td>
          <td>需要相容舊專案</td>
      </tr>
      <tr>
          <td><strong>Hatch</strong></td>
          <td>現代、快速、功能完整</td>
          <td>新專案首選</td>
      </tr>
      <tr>
          <td><strong>Poetry</strong></td>
          <td>依賴鎖定、虛擬環境管理</td>
          <td>需要嚴格依賴控制</td>
      </tr>
      <tr>
          <td><strong>Flit</strong></td>
          <td>極簡、僅純 Python</td>
          <td>簡單函式庫</td>
      </tr>
  </tbody>
</table>
<h2 id="什麼時候該打包成套件">什麼時候該打包成套件？</h2>
<h3 id="適合打包">適合打包</h3>
<ul>
<li>多個專案需要使用相同程式碼</li>
<li>程式碼相對穩定，API 不常變動</li>
<li>需要版本控制和變更追蹤</li>
<li>希望其他人能 pip install 使用</li>
<li>需要明確的相依性管理</li>
</ul>
<h3 id="不建議打包">不建議打包</h3>
<ul>
<li>僅單一專案使用</li>
<li>程式碼還在快速迭代</li>
<li>與專案緊密耦合（如特定的配置路徑）</li>
<li>維護成本超過重用收益</li>
</ul>
<h3 id="決策流程圖">決策流程圖</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">程式碼需要跨專案使用？
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 否 → 保持內部目錄
</span></span><span class="line"><span class="ln">3</span><span class="cl">└── 是 → API 穩定嗎？
</span></span><span class="line"><span class="ln">4</span><span class="cl">         ├── 否 → 等待穩定後再打包
</span></span><span class="line"><span class="ln">5</span><span class="cl">         └── 是 → 有維護能力嗎？
</span></span><span class="line"><span class="ln">6</span><span class="cl">                  ├── 否 → 考慮 monorepo
</span></span><span class="line"><span class="ln">7</span><span class="cl">                  └── 是 → 打包發布</span></span></code></pre></div><h2 id="練習">練習</h2>
<h3 id="基礎練習建立最小的-pyprojecttoml">基礎練習：建立最小的 pyproject.toml</h3>
<p><strong>目標</strong>：為一個簡單的工具函式庫建立 pyproject.toml</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># src/my_utils/__init__.py</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="s2">&#34;&#34;&#34;Simple utilities.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">__version__</span> <span class="o">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">def</span> <span class="nf">greet</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Return a greeting message.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;Hello, </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">!&#34;</span></span></span></code></pre></div><p><strong>要求</strong>：</p>
<ol>
<li>使用 hatchling 作為建構後端</li>
<li>設定專案名稱、版本、描述</li>
<li>指定 Python 版本要求（&gt;=3.10）</li>
</ol>
<details>
<summary>參考答案</summary>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-utils&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Simple utility functions&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.10&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/my_utils&#34;</span><span class="p">]</span></span></span></code></pre></div></details>
<h3 id="進階練習新增-optional-dependencies">進階練習：新增 optional dependencies</h3>
<p><strong>目標</strong>：擴展上面的套件，加入可選的功能</p>
<p><strong>要求</strong>：</p>
<ol>
<li>新增一個需要 <code>requests</code> 的函式</li>
<li>將 <code>requests</code> 設為可選相依性</li>
<li>加入開發相依性（pytest, ruff）</li>
</ol>
<details>
<summary>參考答案</summary>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-utils&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Simple utility functions&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.10&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">http</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&gt;=2.28&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s2">&#34;pytest&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="s2">&#34;ruff&gt;=0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="s2">&#34;my-utils[http]&#34;</span><span class="p">,</span>  <span class="c"># Include http for testing</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/my_utils&#34;</span><span class="p">]</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># src/my_utils/http.py</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="s2">&#34;&#34;&#34;HTTP utilities (requires requests).&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">HAS_REQUESTS</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">HAS_REQUESTS</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">def</span> <span class="nf">fetch_json</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch JSON from URL.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">HAS_REQUESTS</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="k">raise</span> <span class="ne">ImportError</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">            <span class="s2">&#34;requests is required for this feature. &#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">            <span class="s2">&#34;Install with: pip install my-utils[http]&#34;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span><span class="p">()</span></span></span></code></pre></div></details>
<h3 id="挑戰題設定-github-actions-自動發布到-pypi">挑戰題：設定 GitHub Actions 自動發布到 PyPI</h3>
<p><strong>目標</strong>：建立完整的 CI/CD 流程</p>
<p><strong>要求</strong>：</p>
<ol>
<li>Pull Request 時執行測試</li>
<li>建立 Release 時自動發布到 PyPI</li>
<li>使用 Trusted Publishing（不需要 API Token）</li>
<li>多版本 Python 測試矩陣</li>
</ol>
<p><strong>提示</strong>：</p>
<ul>
<li>需要在 PyPI 設定 Trusted Publisher</li>
<li>使用 <code>pypa/gh-action-pypi-publish@release/v1</code></li>
<li>設定 <code>id-token: write</code> 權限</li>
</ul>
<details>
<summary>參考答案</summary>
<ol>
<li>
<p>先在 PyPI 設定 Trusted Publisher：</p>
<ul>
<li>前往 <a href="https://pypi.org/manage/account/publishing/">pypi.org</a></li>
<li>新增 GitHub publisher</li>
<li>填入 repository owner、name、workflow 路徑</li>
</ul>
</li>
<li>
<p>建立工作流程檔案：</p>
</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/ci.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">CI</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;3.10&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.11&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.12&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;3.13&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.python-version }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pip install -e &#34;.[dev]&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">ruff check src/ tests/</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pytest tests/ -v</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/publish.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">release</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">published]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="l">read</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">  </span><span class="nt">publish</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">pypi</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3.12&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pip install build</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">python -m build</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/gh-action-pypi-publish@release/v1</span></span></span></code></pre></div></details>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://packaging.python.org/">Python 打包使用者指南</a></li>
<li><a href="https://peps.python.org/pep-0621/">pyproject.toml 規範 (PEP 621)</a></li>
<li><a href="https://hatch.pypa.io/">Hatch 建置工具</a></li>
<li><a href="https://docs.pypi.org/trusted-publishers/">Trusted Publishers (PyPI)</a></li>
</ul>
<hr>
<p>返回：<a href="/blog/python-advanced/07-packaging/" data-link-title="模組七：打包與發布" data-link-desc="學習現代 Python 套件的打包與發布流程">模組六：打包與發布</a></p>
]]></content:encoded></item><item><title>6.1 pyproject.toml 完整指南</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/pyproject-toml/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/pyproject-toml/</guid><description>&lt;p>本章介紹 pyproject.toml 的結構與設定方式。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>學完本章後，你將能夠：&lt;/p>
&lt;ol>
&lt;li>理解 pyproject.toml 的三個主要表&lt;/li>
&lt;li>設定專案元數據（PEP 621）&lt;/li>
&lt;li>設定建構系統（PEP 518）&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="原理層pyprojecttoml-的演進">【原理層】pyproject.toml 的演進&lt;/h2>
&lt;h3 id="歷史背景">歷史背景&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Python 打包的演進：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── 2000s: setup.py（純 Python 腳本）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 2010s: setup.cfg（宣告式設定）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 2016: PEP 518（pyproject.toml 誕生）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 2020: PEP 621（標準化元數據）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">└── 2025: pyproject.toml 成為主流標準&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="為什麼需要-pyprojecttoml">為什麼需要 pyproject.toml？&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">setup.py 的問題：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 需要執行程式碼才能讀取元數據
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 安全風險（任意程式碼執行）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 無法標準化建構依賴
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">└── 不同工具使用不同設定檔
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">pyproject.toml 的優點：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── 靜態設定，不需執行
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 標準化格式（TOML）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 統一的設定位置
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">└── 支援多種建構後端&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="相關-pep">相關 PEP&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>PEP&lt;/th>
 &lt;th>內容&lt;/th>
 &lt;th>狀態&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>PEP 518&lt;/td>
 &lt;td>build-system 表&lt;/td>
 &lt;td>已採納&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PEP 621&lt;/td>
 &lt;td>project 元數據&lt;/td>
 &lt;td>已採納&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PEP 639&lt;/td>
 &lt;td>license 欄位改進&lt;/td>
 &lt;td>已採納&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PEP 660&lt;/td>
 &lt;td>可編輯安裝&lt;/td>
 &lt;td>已採納&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PEP 735&lt;/td>
 &lt;td>依賴群組&lt;/td>
 &lt;td>草案中&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="設計層三個主要表">【設計層】三個主要表&lt;/h2>
&lt;h3 id="檔案結構總覽">檔案結構總覽&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml 的三個主要表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c"># 定義如何建構套件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c"># 定義套件的元數據&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">xxx&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c"># 各種工具的設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="c"># [tool.setuptools], [tool.pytest], [tool.ruff], etc.&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="build-system建構設定">[build-system]：建構設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c"># 建構時需要的套件（PEP 518）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;wheel&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="c"># 如果有 C 擴展&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="c"># &amp;#34;cython&amp;gt;=3.0&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c"># 建構後端（PEP 517）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c"># 選用：後端路徑（較少使用）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c"># backend-path = [&amp;#34;.&amp;#34;]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>常見的建構後端：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">建構後端 requires
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">─────────────────────────────────────────────────────
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">setuptools.build_meta [&amp;#34;setuptools&amp;gt;=61.0&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">flit_core.buildapi [&amp;#34;flit_core&amp;gt;=3.4&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">hatchling.build [&amp;#34;hatchling&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">poetry.core.masonry.api [&amp;#34;poetry-core&amp;gt;=1.0.0&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">maturin [&amp;#34;maturin&amp;gt;=1.5&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">scikit_build_core.build [&amp;#34;scikit-build-core&amp;gt;=0.5&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">mesonpy [&amp;#34;meson-python&amp;#34;]&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="project專案元數據">[project]：專案元數據&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c"># === 必填欄位 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-awesome-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c"># === 基本資訊 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;A short description of the package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span> &lt;span class="c"># 或 {file = &amp;#34;README.md&amp;#34;, content-type = &amp;#34;text/markdown&amp;#34;}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">text&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;MIT&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c"># 或 {file = &amp;#34;LICENSE&amp;#34;}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c"># === 作者資訊 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;you@example.com&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">maintainers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Maintainer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;maintainer@example.com&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="c"># === 分類與關鍵字 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="nx">keywords&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;example&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;package&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;demo&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="nx">classifiers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Development Status :: 4 - Beta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Intended Audience :: Developers&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;License :: OSI Approved :: MIT License&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.8&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.11&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="c"># === URL ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">urls&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="nx">Homepage&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/project&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">&lt;span class="nx">Documentation&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://project.readthedocs.io&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">&lt;span class="nx">Repository&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/project&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">&lt;span class="nx">Changelog&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/project/blob/main/CHANGELOG.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">&lt;span class="c"># === 依賴 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;requests&amp;gt;=2.28&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;click&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest&amp;gt;=7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest-cov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mypy&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl">&lt;span class="nx">docs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;sphinx&amp;gt;=6.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;sphinx-rtd-theme&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl">&lt;span class="c"># === 入口點 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl">&lt;span class="nx">my-cli&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.cli:main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">gui-scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl">&lt;span class="nx">my-gui&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.gui:main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">65&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">66&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">entry-points&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="s2">&amp;#34;my_package.plugins&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">67&lt;/span>&lt;span class="cl">&lt;span class="nx">plugin1&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.plugins.plugin1:Plugin&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="toolxxx工具設定">[tool.xxx]：工具設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># === setuptools 設定 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c"># 或使用自動發現&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">package-dir&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">packages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">where&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">package-data&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">my_package&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;*.json&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;data/*&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c"># === pytest 設定 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pytest&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ini_options&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">testpaths&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">python_files&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;test_*.py&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="nx">addopts&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;-v --cov=my_package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="c"># === ruff 設定 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="nx">line-length&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">88&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="nx">target-version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;py38&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="nx">select&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;F&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;W&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;I&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;UP&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="nx">ignore&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E501&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="c"># === mypy 設定 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mypy&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="nx">python_version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="nx">strict&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="nx">warn_return_any&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="c"># === coverage 設定 ===&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">coverage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">run&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="nx">source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">&lt;span class="nx">branch&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">coverage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">report&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="nx">exclude_lines&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pragma: no cover&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;if TYPE_CHECKING:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層完整範例">【實作層】完整範例&lt;/h2>
&lt;h3 id="最小可行設定">最小可行設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># 最小的 pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="標準函式庫風格">標準函式庫風格&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;A well-documented Python package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">text&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;MIT&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;you@example.com&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">classifiers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Development Status :: 4 - Beta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Intended Audience :: Developers&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;License :: OSI Approved :: MIT License&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Operating System :: OS Independent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.8&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.11&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Typing :: Typed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;gt;=7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;ruff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;mypy&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">urls&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="nx">Homepage&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="nx">Repository&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">packages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="nx">where&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="含-cli-的應用程式">含 CLI 的應用程式&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-cli-tool&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;2.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;A powerful CLI tool&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">text&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Apache-2.0&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.9&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;CLI Team&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;click&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;rich&amp;gt;=13.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pytest-cov&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="nx">my-tool&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_cli_tool.main:cli&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">urls&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="nx">Homepage&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://my-cli-tool.dev&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">packages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="nx">where&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">package-data&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="nx">my_cli_tool&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;templates/*.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;config/*.yaml&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="進階動態欄位">【進階】動態欄位&lt;/h2>
&lt;h3 id="動態版本">動態版本&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">dynamic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c"># 版本由其他來源決定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dynamic&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">attr&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.__version__&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c"># 或從檔案讀取&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c"># version = {file = &amp;#34;VERSION&amp;#34;}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># src/my_package/__init__.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="n">__version__&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;1.2.3&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="動態依賴">動態依賴&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">dynamic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;dependencies&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;optional-dependencies&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dynamic&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">file&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requirements.txt&amp;#34;&lt;/span>&lt;span class="p">]}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">file&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requirements-dev.txt&amp;#34;&lt;/span>&lt;span class="p">]}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="使用-setuptools-scmgit-標籤版本">使用 setuptools-scm（Git 標籤版本）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;setuptools-scm&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">dynamic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools_scm&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c"># 版本從 git tag 自動產生&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c"># v1.0.0 -&amp;gt; 1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c"># v1.0.0-2-gabcdef -&amp;gt; 1.0.0.dev2+gabcdef&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層專案結構對應">【實作層】專案結構對應&lt;/h2>
&lt;h3 id="flat-layout">Flat Layout&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">my-package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── my_package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">│ └── module.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl"> └── test_module.py&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="src-layout推薦">Src Layout（推薦）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">my-package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">│ └── my_package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">│ └── module.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl"> └── test_module.py&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">packages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">where&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="為什麼推薦-src-layout">為什麼推薦 src layout？&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Src Layout 的優點：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── 避免匯入本地未安裝的套件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 強制測試已安裝的版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 清楚區分原始碼和專案根目錄
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">└── 避免名稱衝突&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="進階pep-639-授權條款">【進階】PEP 639 授權條款&lt;/h2>
&lt;h3 id="新的-license-語法">新的 license 語法&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># PEP 639（Python 3.14+，但建構工具已支援）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c"># SPDX 表示法&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;MIT&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c"># 或&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Apache-2.0 OR MIT&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c"># 或&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;GPL-3.0-only&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c"># 授權檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">license-files&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;LICENSE&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;LICENSES/*&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常見的-spdx-識別碼">常見的 SPDX 識別碼&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">識別碼 完整名稱
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">─────────────────────────────────────────
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">MIT MIT License
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">Apache-2.0 Apache License 2.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">GPL-3.0-only GNU GPL v3.0 only
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">GPL-3.0-or-later GNU GPL v3.0 or later
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">BSD-3-Clause BSD 3-Clause License
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">BSD-2-Clause BSD 2-Clause License
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">MPL-2.0 Mozilla Public License 2.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">LGPL-3.0-only GNU LGPL v3.0 only
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">ISC ISC License
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">Unlicense The Unlicense&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="驗證檢查設定">【驗證】檢查設定&lt;/h2>
&lt;h3 id="使用-validate-pyproject">使用 validate-pyproject&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">pip install validate-pyproject
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># 驗證 pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">validate-pyproject pyproject.toml&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="使用-build-測試建構">使用 build 測試建構&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">pip install build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構套件（會驗證設定）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">python -m build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構結果&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">ls dist/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># my_package-1.0.0.tar.gz&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c1"># my_package-1.0.0-py3-none-any.whl&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常見錯誤">常見錯誤&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">錯誤：Unknown key in [project]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">原因：使用了非標準欄位
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">解決：檢查 PEP 621 允許的欄位
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">錯誤：Invalid version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">原因：版本格式不符合 PEP 440
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">解決：使用正確格式，如 &amp;#34;1.0.0&amp;#34;, &amp;#34;1.0.0a1&amp;#34;, &amp;#34;1.0.0.dev1&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">錯誤：Missing required key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">原因：缺少必填欄位
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">解決：至少要有 name 和 version（或 dynamic）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="思考題">思考題&lt;/h2>
&lt;ol>
&lt;li>為什麼 Python 社群花了這麼長時間才標準化打包設定？&lt;/li>
&lt;li>&lt;code>[build-system]&lt;/code> 中的 &lt;code>requires&lt;/code> 和 &lt;code>[project]&lt;/code> 中的 &lt;code>dependencies&lt;/code> 有什麼區別？&lt;/li>
&lt;li>動態欄位在什麼情況下有用？有什麼潛在問題？&lt;/li>
&lt;/ol>
&lt;h2 id="實作練習">實作練習&lt;/h2>
&lt;ol>
&lt;li>將一個使用 setup.py 的舊專案遷移到 pyproject.toml&lt;/li>
&lt;li>建立一個包含 CLI 入口點的套件，並在本地測試安裝&lt;/li>
&lt;li>使用 setuptools-scm 設定自動版本管理&lt;/li>
&lt;/ol>
&lt;h2 id="延伸閱讀">延伸閱讀&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://peps.python.org/pep-0518/">PEP 518 - build-system&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://peps.python.org/pep-0621/">PEP 621 - project metadata&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://peps.python.org/pep-0639/">PEP 639 - license&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/">Python Packaging Guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>下一章：&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">建構系統比較&lt;/a>&lt;/p></description><content:encoded><![CDATA[<p>本章介紹 pyproject.toml 的結構與設定方式。</p>
<h2 id="本章目標">本章目標</h2>
<p>學完本章後，你將能夠：</p>
<ol>
<li>理解 pyproject.toml 的三個主要表</li>
<li>設定專案元數據（PEP 621）</li>
<li>設定建構系統（PEP 518）</li>
</ol>
<hr>
<h2 id="原理層pyprojecttoml-的演進">【原理層】pyproject.toml 的演進</h2>
<h3 id="歷史背景">歷史背景</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Python 打包的演進：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 2000s: setup.py（純 Python 腳本）
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 2010s: setup.cfg（宣告式設定）
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 2016: PEP 518（pyproject.toml 誕生）
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 2020: PEP 621（標準化元數據）
</span></span><span class="line"><span class="ln">6</span><span class="cl">└── 2025: pyproject.toml 成為主流標準</span></span></code></pre></div><h3 id="為什麼需要-pyprojecttoml">為什麼需要 pyproject.toml？</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">setup.py 的問題：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 需要執行程式碼才能讀取元數據
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 安全風險（任意程式碼執行）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 無法標準化建構依賴
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── 不同工具使用不同設定檔
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">pyproject.toml 的優點：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 靜態設定，不需執行
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 標準化格式（TOML）
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 統一的設定位置
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── 支援多種建構後端</span></span></code></pre></div><h3 id="相關-pep">相關 PEP</h3>
<table>
  <thead>
      <tr>
          <th>PEP</th>
          <th>內容</th>
          <th>狀態</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>PEP 518</td>
          <td>build-system 表</td>
          <td>已採納</td>
      </tr>
      <tr>
          <td>PEP 621</td>
          <td>project 元數據</td>
          <td>已採納</td>
      </tr>
      <tr>
          <td>PEP 639</td>
          <td>license 欄位改進</td>
          <td>已採納</td>
      </tr>
      <tr>
          <td>PEP 660</td>
          <td>可編輯安裝</td>
          <td>已採納</td>
      </tr>
      <tr>
          <td>PEP 735</td>
          <td>依賴群組</td>
          <td>草案中</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="設計層三個主要表">【設計層】三個主要表</h2>
<h3 id="檔案結構總覽">檔案結構總覽</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml 的三個主要表</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c"># 定義如何建構套件</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c"># 定義套件的元數據</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c"># ...</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">xxx</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c"># 各種工具的設定</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c"># [tool.setuptools], [tool.pytest], [tool.ruff], etc.</span></span></span></code></pre></div><h3 id="build-system建構設定">[build-system]：建構設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># 建構時需要的套件（PEP 518）</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="s2">&#34;wheel&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="c"># 如果有 C 擴展</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="c"># &#34;cython&gt;=3.0&#34;,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c"># 建構後端（PEP 517）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c"># 選用：後端路徑（較少使用）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c"># backend-path = [&#34;.&#34;]</span></span></span></code></pre></div><p>常見的建構後端：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">建構後端                     requires
</span></span><span class="line"><span class="ln">2</span><span class="cl">─────────────────────────────────────────────────────
</span></span><span class="line"><span class="ln">3</span><span class="cl">setuptools.build_meta       [&#34;setuptools&gt;=61.0&#34;]
</span></span><span class="line"><span class="ln">4</span><span class="cl">flit_core.buildapi          [&#34;flit_core&gt;=3.4&#34;]
</span></span><span class="line"><span class="ln">5</span><span class="cl">hatchling.build             [&#34;hatchling&#34;]
</span></span><span class="line"><span class="ln">6</span><span class="cl">poetry.core.masonry.api     [&#34;poetry-core&gt;=1.0.0&#34;]
</span></span><span class="line"><span class="ln">7</span><span class="cl">maturin                     [&#34;maturin&gt;=1.5&#34;]
</span></span><span class="line"><span class="ln">8</span><span class="cl">scikit_build_core.build     [&#34;scikit-build-core&gt;=0.5&#34;]
</span></span><span class="line"><span class="ln">9</span><span class="cl">mesonpy                     [&#34;meson-python&#34;]</span></span></code></pre></div><h3 id="project專案元數據">[project]：專案元數據</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># === 必填欄位 ===</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-awesome-package&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c"># === 基本資訊 ===</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;A short description of the package&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>  <span class="c"># 或 {file = &#34;README.md&#34;, content-type = &#34;text/markdown&#34;}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="p">{</span><span class="nx">text</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span><span class="p">}</span>  <span class="c"># 或 {file = &#34;LICENSE&#34;}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.8&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c"># === 作者資訊 ===</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">{</span><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;you@example.com&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">maintainers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">{</span><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Maintainer&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;maintainer@example.com&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c"># === 分類與關鍵字 ===</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nx">keywords</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;example&#34;</span><span class="p">,</span> <span class="s2">&#34;package&#34;</span><span class="p">,</span> <span class="s2">&#34;demo&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">classifiers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="s2">&#34;Development Status :: 4 - Beta&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">&#34;Intended Audience :: Developers&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="s2">&#34;License :: OSI Approved :: MIT License&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.8&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.11&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.12&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c"># === URL ===</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">urls</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="nx">Homepage</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/project&#34;</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="nx">Documentation</span> <span class="p">=</span> <span class="s2">&#34;https://project.readthedocs.io&#34;</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="nx">Repository</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/project&#34;</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="nx">Changelog</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/project/blob/main/CHANGELOG.md&#34;</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c"># === 依賴 ===</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">    <span class="s2">&#34;requests&gt;=2.28&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">    <span class="s2">&#34;click&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="s2">&#34;pytest&gt;=7.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">    <span class="s2">&#34;pytest-cov&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="s2">&#34;ruff&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="s2">&#34;mypy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="nx">docs</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">    <span class="s2">&#34;sphinx&gt;=6.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">    <span class="s2">&#34;sphinx-rtd-theme&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">
</span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="c"># === 入口點 ===</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="nx">my-cli</span> <span class="p">=</span> <span class="s2">&#34;my_package.cli:main&#34;</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">gui-scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="nx">my-gui</span> <span class="p">=</span> <span class="s2">&#34;my_package.gui:main&#34;</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">
</span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">entry-points</span><span class="p">.</span><span class="s2">&#34;my_package.plugins&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="nx">plugin1</span> <span class="p">=</span> <span class="s2">&#34;my_package.plugins.plugin1:Plugin&#34;</span></span></span></code></pre></div><h3 id="toolxxx工具設定">[tool.xxx]：工具設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># === setuptools 設定 ===</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;my_package&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c"># 或使用自動發現</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">package-dir</span> <span class="p">=</span> <span class="p">{</span><span class="s2">&#34;&#34;</span> <span class="p">=</span> <span class="s2">&#34;src&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">packages</span><span class="p">.</span><span class="nx">find</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">where</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">package-data</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">my_package</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;*.json&#34;</span><span class="p">,</span> <span class="s2">&#34;data/*&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c"># === pytest 設定 ===</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">pytest</span><span class="p">.</span><span class="nx">ini_options</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">testpaths</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;tests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">python_files</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;test_*.py&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nx">addopts</span> <span class="p">=</span> <span class="s2">&#34;-v --cov=my_package&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c"># === ruff 設定 ===</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nx">line-length</span> <span class="p">=</span> <span class="mi">88</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">target-version</span> <span class="p">=</span> <span class="s2">&#34;py38&#34;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="nx">select</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;E&#34;</span><span class="p">,</span> <span class="s2">&#34;F&#34;</span><span class="p">,</span> <span class="s2">&#34;W&#34;</span><span class="p">,</span> <span class="s2">&#34;I&#34;</span><span class="p">,</span> <span class="s2">&#34;UP&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="nx">ignore</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;E501&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c"># === mypy 設定 ===</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">mypy</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">python_version</span> <span class="p">=</span> <span class="s2">&#34;3.8&#34;</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="nx">strict</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="nx">warn_return_any</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c"># === coverage 設定 ===</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">coverage</span><span class="p">.</span><span class="nx">run</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="nx">source</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;my_package&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="nx">branch</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">coverage</span><span class="p">.</span><span class="nx">report</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="nx">exclude_lines</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="s2">&#34;pragma: no cover&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="s2">&#34;if TYPE_CHECKING:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="p">]</span></span></span></code></pre></div><hr>
<h2 id="實作層完整範例">【實作層】完整範例</h2>
<h3 id="最小可行設定">最小可行設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># 最小的 pyproject.toml</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span></span></span></code></pre></div><h3 id="標準函式庫風格">標準函式庫風格</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;A well-documented Python package&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="p">{</span><span class="nx">text</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.8&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[{</span><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;you@example.com&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">classifiers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;Development Status :: 4 - Beta&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s2">&#34;Intended Audience :: Developers&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="s2">&#34;License :: OSI Approved :: MIT License&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="s2">&#34;Operating System :: OS Independent&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.8&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.11&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.12&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">&#34;Typing :: Typed&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&gt;=7.0&#34;</span><span class="p">,</span> <span class="s2">&#34;ruff&#34;</span><span class="p">,</span> <span class="s2">&#34;mypy&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">urls</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="nx">Homepage</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/my-package&#34;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="nx">Repository</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/my-package&#34;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">packages</span><span class="p">.</span><span class="nx">find</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="nx">where</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="含-cli-的應用程式">含 CLI 的應用程式</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-cli-tool&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;2.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;A powerful CLI tool&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="p">{</span><span class="nx">text</span> <span class="p">=</span> <span class="s2">&#34;Apache-2.0&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.9&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[{</span><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;CLI Team&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;click&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s2">&#34;rich&gt;=13.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&#34;</span><span class="p">,</span> <span class="s2">&#34;pytest-cov&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">my-tool</span> <span class="p">=</span> <span class="s2">&#34;my_cli_tool.main:cli&#34;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">urls</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="nx">Homepage</span> <span class="p">=</span> <span class="s2">&#34;https://my-cli-tool.dev&#34;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">packages</span><span class="p">.</span><span class="nx">find</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="nx">where</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">package-data</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="nx">my_cli_tool</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;templates/*.txt&#34;</span><span class="p">,</span> <span class="s2">&#34;config/*.yaml&#34;</span><span class="p">]</span></span></span></code></pre></div><hr>
<h2 id="進階動態欄位">【進階】動態欄位</h2>
<h3 id="動態版本">動態版本</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>  <span class="c"># 版本由其他來源決定</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">dynamic</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="p">{</span><span class="nx">attr</span> <span class="p">=</span> <span class="s2">&#34;my_package.__version__&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c"># 或從檔案讀取</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c"># version = {file = &#34;VERSION&#34;}</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># src/my_package/__init__.py</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">__version__</span> <span class="o">=</span> <span class="s2">&#34;1.2.3&#34;</span></span></span></code></pre></div><h3 id="動態依賴">動態依賴</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;dependencies&#34;</span><span class="p">,</span> <span class="s2">&#34;optional-dependencies&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">dynamic</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">{</span><span class="nx">file</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requirements.txt&#34;</span><span class="p">]}</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nx">optional-dependencies</span><span class="p">.</span><span class="nx">dev</span> <span class="p">=</span> <span class="p">{</span><span class="nx">file</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requirements-dev.txt&#34;</span><span class="p">]}</span></span></span></code></pre></div><h3 id="使用-setuptools-scmgit-標籤版本">使用 setuptools-scm（Git 標籤版本）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">,</span> <span class="s2">&#34;setuptools-scm&gt;=8.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools_scm</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c"># 版本從 git tag 自動產生</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c"># v1.0.0 -&gt; 1.0.0</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c"># v1.0.0-2-gabcdef -&gt; 1.0.0.dev2+gabcdef</span></span></span></code></pre></div><hr>
<h2 id="實作層專案結構對應">【實作層】專案結構對應</h2>
<h3 id="flat-layout">Flat Layout</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">my-package/
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── my_package/
</span></span><span class="line"><span class="ln">5</span><span class="cl">│   ├── __init__.py
</span></span><span class="line"><span class="ln">6</span><span class="cl">│   └── module.py
</span></span><span class="line"><span class="ln">7</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln">8</span><span class="cl">    └── test_module.py</span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;my_package&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="src-layout推薦">Src Layout（推薦）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">my-package/
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln">5</span><span class="cl">│   └── my_package/
</span></span><span class="line"><span class="ln">6</span><span class="cl">│       ├── __init__.py
</span></span><span class="line"><span class="ln">7</span><span class="cl">│       └── module.py
</span></span><span class="line"><span class="ln">8</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln">9</span><span class="cl">    └── test_module.py</span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">packages</span><span class="p">.</span><span class="nx">find</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">where</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="為什麼推薦-src-layout">為什麼推薦 src layout？</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Src Layout 的優點：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 避免匯入本地未安裝的套件
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 強制測試已安裝的版本
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 清楚區分原始碼和專案根目錄
</span></span><span class="line"><span class="ln">5</span><span class="cl">└── 避免名稱衝突</span></span></code></pre></div><hr>
<h2 id="進階pep-639-授權條款">【進階】PEP 639 授權條款</h2>
<h3 id="新的-license-語法">新的 license 語法</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># PEP 639（Python 3.14+，但建構工具已支援）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c"># SPDX 表示法</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c"># 或</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;Apache-2.0 OR MIT&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c"># 或</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;GPL-3.0-only&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c"># 授權檔案</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">license-files</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;LICENSE&#34;</span><span class="p">,</span> <span class="s2">&#34;LICENSES/*&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="常見的-spdx-識別碼">常見的 SPDX 識別碼</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">識別碼              完整名稱
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">─────────────────────────────────────────
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">MIT                 MIT License
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">Apache-2.0          Apache License 2.0
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">GPL-3.0-only        GNU GPL v3.0 only
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">GPL-3.0-or-later    GNU GPL v3.0 or later
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">BSD-3-Clause        BSD 3-Clause License
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">BSD-2-Clause        BSD 2-Clause License
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">MPL-2.0             Mozilla Public License 2.0
</span></span><span class="line"><span class="ln">10</span><span class="cl">LGPL-3.0-only       GNU LGPL v3.0 only
</span></span><span class="line"><span class="ln">11</span><span class="cl">ISC                 ISC License
</span></span><span class="line"><span class="ln">12</span><span class="cl">Unlicense           The Unlicense</span></span></code></pre></div><hr>
<h2 id="驗證檢查設定">【驗證】檢查設定</h2>
<h3 id="使用-validate-pyproject">使用 validate-pyproject</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">pip install validate-pyproject
</span></span><span class="line"><span class="ln">2</span><span class="cl">
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 驗證 pyproject.toml</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">validate-pyproject pyproject.toml</span></span></code></pre></div><h3 id="使用-build-測試建構">使用 build 測試建構</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">pip install build
</span></span><span class="line"><span class="ln">2</span><span class="cl">
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 建構套件（會驗證設定）</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">python -m build
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># 建構結果</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">ls dist/
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># my_package-1.0.0.tar.gz</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"># my_package-1.0.0-py3-none-any.whl</span></span></span></code></pre></div><h3 id="常見錯誤">常見錯誤</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">錯誤：Unknown key in [project]
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">原因：使用了非標準欄位
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">解決：檢查 PEP 621 允許的欄位
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">錯誤：Invalid version
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">原因：版本格式不符合 PEP 440
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">解決：使用正確格式，如 &#34;1.0.0&#34;, &#34;1.0.0a1&#34;, &#34;1.0.0.dev1&#34;
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">錯誤：Missing required key
</span></span><span class="line"><span class="ln">10</span><span class="cl">原因：缺少必填欄位
</span></span><span class="line"><span class="ln">11</span><span class="cl">解決：至少要有 name 和 version（或 dynamic）</span></span></code></pre></div><hr>
<h2 id="思考題">思考題</h2>
<ol>
<li>為什麼 Python 社群花了這麼長時間才標準化打包設定？</li>
<li><code>[build-system]</code> 中的 <code>requires</code> 和 <code>[project]</code> 中的 <code>dependencies</code> 有什麼區別？</li>
<li>動態欄位在什麼情況下有用？有什麼潛在問題？</li>
</ol>
<h2 id="實作練習">實作練習</h2>
<ol>
<li>將一個使用 setup.py 的舊專案遷移到 pyproject.toml</li>
<li>建立一個包含 CLI 入口點的套件，並在本地測試安裝</li>
<li>使用 setuptools-scm 設定自動版本管理</li>
</ol>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://peps.python.org/pep-0518/">PEP 518 - build-system</a></li>
<li><a href="https://peps.python.org/pep-0621/">PEP 621 - project metadata</a></li>
<li><a href="https://peps.python.org/pep-0639/">PEP 639 - license</a></li>
<li><a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/">Python Packaging Guide</a></li>
</ul>
<hr>
<p>下一章：<a href="/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">建構系統比較</a></p>
]]></content:encoded></item><item><title>案例：使用 Poetry 完整工作流</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/poetry-workflow/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/poetry-workflow/</guid><description>&lt;p>本案例展示如何使用 Poetry 管理現代 Python 專案的完整生命週期，從專案建立到套件發布。&lt;/p>
&lt;h2 id="先備知識">先備知識&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">建構系統比較&lt;/a>&lt;/li>
&lt;li>Python 虛擬環境基礎&lt;/li>
&lt;li>套件相依性概念&lt;/li>
&lt;/ul>
&lt;h2 id="問題背景">問題背景&lt;/h2>
&lt;h3 id="現代專案的挑戰">現代專案的挑戰&lt;/h3>
&lt;p>傳統的 Python 專案管理面臨以下問題：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">傳統工作流程的痛點：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 相依性管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">│ ├── requirements.txt 無法鎖定間接相依性
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">│ ├── pip freeze 產生的版本可能過於嚴格
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">│ └── 開發與生產環境相依性混在一起
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── 虛擬環境
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ ├── 需要手動建立和管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">│ ├── 不同專案的環境容易混淆
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ └── 缺乏與專案的關聯性
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 建構與發布
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│ ├── setup.py 設定複雜
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">│ ├── 發布流程需要多個工具
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">│ └── 版本管理不一致
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">└── 團隊協作
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> ├── 環境難以重現
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> ├── 「在我電腦上可以跑」問題
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> └── 新成員上手困難&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="為什麼選擇-poetry">為什麼選擇 Poetry？&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>特性&lt;/th>
 &lt;th>pip + venv&lt;/th>
 &lt;th>Poetry&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>相依性鎖定&lt;/td>
 &lt;td>手動（pip freeze）&lt;/td>
 &lt;td>自動（poetry.lock）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>間接相依性追蹤&lt;/td>
 &lt;td>無&lt;/td>
 &lt;td>完整追蹤&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>虛擬環境管理&lt;/td>
 &lt;td>手動&lt;/td>
 &lt;td>自動整合&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>建構系統&lt;/td>
 &lt;td>需要額外工具&lt;/td>
 &lt;td>內建&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>發布流程&lt;/td>
 &lt;td>需要 twine&lt;/td>
 &lt;td>內建&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>相依性群組&lt;/td>
 &lt;td>無原生支援&lt;/td>
 &lt;td>完整支援&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="解決方案">解決方案&lt;/h2>
&lt;h3 id="安裝-poetry">安裝 Poetry&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 官方推薦的安裝方式（獨立安裝）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">curl -sSL https://install.python-poetry.org &lt;span class="p">|&lt;/span> python3 -
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或使用 pipx（推薦用於 CLI 工具）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">pipx install poetry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 確認安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">poetry --version&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="設定-poetry">設定 Poetry&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在專案目錄中建立虛擬環境（推薦）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">poetry config virtualenvs.in-project &lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 查看所有設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">poetry config --list&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="工作流一建立新專案">工作流一：建立新專案&lt;/h3>
&lt;h4 id="使用-poetry-new完整專案結構">使用 poetry new（完整專案結構）&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建立新專案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry new my-awesome-lib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 產生的目錄結構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">my-awesome-lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── my_awesome_lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ └── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> └── __init__.py&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 src layout（推薦用於函式庫）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry new --src my-awesome-lib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 產生的目錄結構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">my-awesome-lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ └── my_awesome_lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">│ └── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> └── __init__.py&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="使用-poetry-init現有專案">使用 poetry init（現有專案）&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在現有目錄初始化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nb">cd&lt;/span> existing-project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">poetry init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 互動式問答&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># - Package name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># - Version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># - Description&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># - Author&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># - License&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c1"># - Python version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># - Dependencies&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="互動式初始化範例">互動式初始化範例&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">This command will guide you through creating your pyproject.toml config.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">Package name [existing-project]: my-package
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">Version [0.1.0]: 1.0.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">Description []: A useful Python package
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">Author [Your Name &amp;lt;you@example.com&amp;gt;, n to skip]:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">License []: MIT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">Compatible Python versions [^3.10]: ^3.10
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">Would you like to define your main dependencies interactively? (yes/no) [yes]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">Search for package to add (or leave blank to continue): requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">...&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="工作流二管理相依性">工作流二：管理相依性&lt;/h3>
&lt;h4 id="新增相依性">新增相依性&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 新增生產相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry add requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">poetry add &lt;span class="s2">&amp;#34;httpx&amp;gt;=0.24&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 新增開發相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">poetry add pytest --group dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">poetry add ruff mypy --group dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># 新增文件相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">poetry add mkdocs mkdocs-material --group docs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># 新增可選相依性（extras）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">poetry add pyyaml --optional&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="pyprojecttoml-的變化">pyproject.toml 的變化&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requests&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^2.31.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">httpx&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=0.24&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^8.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">ruff&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^0.4.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">mypy&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^1.10.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">mkdocs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^1.5.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">mkdocs-material&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^9.5.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">extras&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">yaml&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pyyaml&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="移除相依性">移除相依性&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 移除生產相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">poetry remove requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 移除開發相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">poetry remove pytest --group dev&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="更新相依性">更新相依性&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 更新所有相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 更新特定套件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">poetry update requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 檢視可更新的套件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">poetry show --outdated
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 檢視相依性樹&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">poetry show --tree&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="相依性樹範例輸出">相依性樹範例輸出&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">requests 2.31.0 Python HTTP for Humans.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── certifi &amp;gt;=2017.4.17
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── charset-normalizer &amp;gt;=2,&amp;lt;4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── idna &amp;gt;=2.5,&amp;lt;4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">└── urllib3 &amp;gt;=1.21.1,&amp;lt;3&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="工作流三虛擬環境管理">工作流三：虛擬環境管理&lt;/h3>
&lt;h4 id="自動環境管理">自動環境管理&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝所有相依性（自動建立虛擬環境）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 僅安裝生產相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">poetry install --only main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝包含特定群組&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">poetry install --with dev,docs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 排除特定群組&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">poetry install --without docs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 extras&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">poetry install --extras yaml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">poetry install --all-extras&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="環境操作">環境操作&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 進入虛擬環境 shell&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry shell
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在虛擬環境中執行命令（不進入 shell）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">poetry run python script.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">poetry run pytest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">poetry run python -c &lt;span class="s2">&amp;#34;import my_package; print(my_package.__version__)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># 顯示環境資訊&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">poetry env info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># 顯示環境路徑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">poetry env info --path
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c1"># 列出所有環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">poetry env list
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c1"># 切換 Python 版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">poetry env use python3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">poetry env use 3.12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="c1"># 刪除環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">poetry env remove python3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">poetry env remove --all&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="環境資訊範例輸出">環境資訊範例輸出&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">Virtualenv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">Python: 3.11.6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">Implementation: CPython
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">Path: /path/to/project/.venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">Executable: /path/to/project/.venv/bin/python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">Valid: True
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">Base
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">Platform: darwin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">OS: posix
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">Python: 3.11.6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">Path: /opt/homebrew/Cellar/python@3.11/3.11.6/Frameworks/Python.framework/Versions/3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">Executable: /opt/homebrew/Cellar/python@3.11/3.11.6/Frameworks/Python.framework/Versions/3.11/bin/python3.11&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="工作流四建構與發布">工作流四：建構與發布&lt;/h3>
&lt;h4 id="建構套件">建構套件&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構 sdist 和 wheel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 僅建構 wheel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">poetry build --format wheel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構結果&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">dist/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── my_package-1.0.0-py3-none-any.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">└── my_package-1.0.0.tar.gz&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="發布到-pypi">發布到 PyPI&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 設定 PyPI token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry config pypi-token.pypi pypi-XXXXXXXXXXXX
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布到 PyPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">poetry publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構並發布（一步完成）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">poetry publish --build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布到 TestPyPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">poetry config repositories.testpypi https://test.pypi.org/legacy/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">poetry config pypi-token.testpypi pypi-XXXXXXXXXXXX
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">poetry publish --repository testpypi&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="版本管理">版本管理&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 查看當前版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 升級版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">poetry version patch &lt;span class="c1"># 1.0.0 -&amp;gt; 1.0.1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">poetry version minor &lt;span class="c1"># 1.0.0 -&amp;gt; 1.1.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">poetry version major &lt;span class="c1"># 1.0.0 -&amp;gt; 2.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># 設定特定版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">poetry version 2.0.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># 預發布版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">poetry version prepatch &lt;span class="c1"># 1.0.0 -&amp;gt; 1.0.1a0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">poetry version preminor &lt;span class="c1"># 1.0.0 -&amp;gt; 1.1.0a0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">poetry version premajor &lt;span class="c1"># 1.0.0 -&amp;gt; 2.0.0a0&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="完整-pyprojecttoml-範例">完整 pyproject.toml 範例&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;poetry-core&amp;gt;=2.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;poetry.core.masonry.api&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-awesome-lib&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;A feature-rich Python library&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;MIT&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;you@example.com&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">keywords&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;library&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;utilities&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">classifiers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Development Status :: 4 - Beta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Intended Audience :: Developers&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;License :: OSI Approved :: MIT License&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Operating System :: OS Independent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.11&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.13&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Typing :: Typed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">urls&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="nx">Homepage&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/my-awesome-lib&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="nx">Documentation&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://my-awesome-lib.readthedocs.io&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="nx">Repository&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/my-awesome-lib.git&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="nx">Changelog&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/my-awesome-lib/blob/main/CHANGELOG.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="nx">my-cli&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_awesome_lib.cli:main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">&lt;span class="nx">yaml&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pyyaml&amp;gt;=6.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="nx">all&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my-awesome-lib[yaml]&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl">&lt;span class="c"># ===== Poetry 特定設定 =====&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nx">include&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_awesome_lib&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl">&lt;span class="nx">python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl">&lt;span class="nx">requests&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^2.31&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl">&lt;span class="nx">click&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^8.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^8.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest-cov&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^4.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest-asyncio&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^0.23&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl">&lt;span class="nx">mypy&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^1.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">&lt;span class="nx">ruff&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^0.4&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl">&lt;span class="nx">optional&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl">&lt;span class="nx">mkdocs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^1.5&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl">&lt;span class="nx">mkdocs-material&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^9.5&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">65&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">66&lt;/span>&lt;span class="cl">&lt;span class="c"># ===== 工具設定 =====&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">67&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">68&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">69&lt;/span>&lt;span class="cl">&lt;span class="nx">src&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">70&lt;/span>&lt;span class="cl">&lt;span class="nx">line-length&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">88&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">71&lt;/span>&lt;span class="cl">&lt;span class="nx">target-version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;py310&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">72&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">73&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">74&lt;/span>&lt;span class="cl">&lt;span class="nx">select&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;W&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;F&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;I&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;B&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;C4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;UP&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">75&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">76&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mypy&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">77&lt;/span>&lt;span class="cl">&lt;span class="nx">python_version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">78&lt;/span>&lt;span class="cl">&lt;span class="nx">strict&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">79&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">80&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pytest&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ini_options&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">81&lt;/span>&lt;span class="cl">&lt;span class="nx">testpaths&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">82&lt;/span>&lt;span class="cl">&lt;span class="nx">addopts&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;-v --tb=short&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="與其他工具的比較">與其他工具的比較&lt;/h2>
&lt;h3 id="poetry-vs-pip">Poetry vs pip&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>操作&lt;/th>
 &lt;th>pip&lt;/th>
 &lt;th>Poetry&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>安裝相依性&lt;/td>
 &lt;td>&lt;code>pip install -r requirements.txt&lt;/code>&lt;/td>
 &lt;td>&lt;code>poetry install&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>新增相依性&lt;/td>
 &lt;td>手動編輯 requirements.txt&lt;/td>
 &lt;td>&lt;code>poetry add package&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>鎖定版本&lt;/td>
 &lt;td>&lt;code>pip freeze &amp;gt; requirements.txt&lt;/code>&lt;/td>
 &lt;td>自動更新 poetry.lock&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>建立環境&lt;/td>
 &lt;td>&lt;code>python -m venv .venv&lt;/code>&lt;/td>
 &lt;td>自動建立&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>執行命令&lt;/td>
 &lt;td>&lt;code>source .venv/bin/activate &amp;amp;&amp;amp; python&lt;/code>&lt;/td>
 &lt;td>&lt;code>poetry run python&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>建構套件&lt;/td>
 &lt;td>&lt;code>python -m build&lt;/code>&lt;/td>
 &lt;td>&lt;code>poetry build&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>發布套件&lt;/td>
 &lt;td>&lt;code>twine upload dist/*&lt;/code>&lt;/td>
 &lt;td>&lt;code>poetry publish&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="poetry-vs-setuptools">Poetry vs setuptools&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>setuptools&lt;/th>
 &lt;th>Poetry&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>設定格式&lt;/td>
 &lt;td>pyproject.toml + 可能需要 setup.py&lt;/td>
 &lt;td>僅 pyproject.toml&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>相依性管理&lt;/td>
 &lt;td>需要額外工具&lt;/td>
 &lt;td>內建完整支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>環境管理&lt;/td>
 &lt;td>無&lt;/td>
 &lt;td>內建&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>學習曲線&lt;/td>
 &lt;td>較陡（歷史包袱）&lt;/td>
 &lt;td>較平緩&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>C 擴展支援&lt;/td>
 &lt;td>完整&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>生態系統&lt;/td>
 &lt;td>最廣泛&lt;/td>
 &lt;td>持續成長&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="poetry-vs-hatch">Poetry vs Hatch&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Hatch&lt;/th>
 &lt;th>Poetry&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>設計理念&lt;/td>
 &lt;td>PEP 標準優先&lt;/td>
 &lt;td>使用者體驗優先&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>相依性鎖定&lt;/td>
 &lt;td>無內建&lt;/td>
 &lt;td>核心功能&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>環境管理&lt;/td>
 &lt;td>多環境（類似 tox）&lt;/td>
 &lt;td>單一虛擬環境&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>腳本系統&lt;/td>
 &lt;td>完整&lt;/td>
 &lt;td>基本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>建構後端&lt;/td>
 &lt;td>hatchling&lt;/td>
 &lt;td>poetry-core&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>適用場景&lt;/td>
 &lt;td>開源函式庫&lt;/td>
 &lt;td>應用程式、內部工具&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="實用技巧">實用技巧&lt;/h2>
&lt;h3 id="技巧一善用-lock-檔案">技巧一：善用 Lock 檔案&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># poetry.lock 的重要性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1"># - 記錄所有相依性的精確版本（包含間接相依性）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c1"># - 確保團隊成員、CI/CD 使用相同版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># - 應該提交到版本控制&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># 根據 lock 檔案安裝（不更新）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">poetry install --no-update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># 驗證 lock 檔案與 pyproject.toml 一致&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">poetry check --lock
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># 匯出為 requirements.txt（部署用）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">poetry &lt;span class="nb">export&lt;/span> -f requirements.txt -o requirements.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">poetry &lt;span class="nb">export&lt;/span> -f requirements.txt --with dev -o requirements-dev.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">poetry &lt;span class="nb">export&lt;/span> --without-hashes -o requirements.txt&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="技巧二善用相依性群組">技巧二：善用相依性群組&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># 開發相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^8.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">ruff&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^0.4&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c"># 可選群組（預設不安裝）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">optional&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">mkdocs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^1.5&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c"># CI 專用群組&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ci&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">optional&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ci&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest-cov&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^4.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">codecov&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^2.1&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝特定群組&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">poetry install --with docs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">poetry install --with ci
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># CI 環境中的安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">poetry install --only main,ci&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="技巧三善用-extras">技巧三：善用 Extras&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c"># 功能性 extras&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">yaml&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pyyaml&amp;gt;=6.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="nx">async&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;httpx&amp;gt;=0.24&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;aiofiles&amp;gt;=23.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c"># 完整安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nx">all&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my-package[yaml,async]&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用者安裝方式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">pip install my-package &lt;span class="c1"># 基本功能&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">pip install &lt;span class="s2">&amp;#34;my-package[yaml]&amp;#34;&lt;/span> &lt;span class="c1"># 包含 YAML 支援&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">pip install &lt;span class="s2">&amp;#34;my-package[all]&amp;#34;&lt;/span> &lt;span class="c1"># 所有功能&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="技巧四本地相依性和-git-相依性">技巧四：本地相依性和 Git 相依性&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c"># 本地路徑相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">my-local-lib&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;../my-local-lib&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">develop&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c"># Git 相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nx">my-git-lib&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">git&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/repo.git&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nx">my-git-lib&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">git&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/repo.git&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">branch&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;develop&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="nx">my-git-lib&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">git&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/repo.git&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">tag&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;v1.0.0&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="nx">my-git-lib&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">git&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/user/repo.git&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;abc123&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="技巧五平台特定相依性">技巧五：平台特定相依性&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c"># 僅 Windows&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">pywin32&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^306&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">markers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;sys_platform == &amp;#39;win32&amp;#39;&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c"># 僅 Linux&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nx">uvloop&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^0.19&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">markers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;sys_platform == &amp;#39;linux&amp;#39;&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c"># Python 版本限制&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="nx">typing-extensions&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^4.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;3.11&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="技巧六poetry-腳本">技巧六：Poetry 腳本&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">my-cli&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.cli:main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">my-tool&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.tools:run&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># src/my_package/cli.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">click&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nd">@click.command&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nd">@click.option&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;--name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">default&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;World&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">help&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Name to greet&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Greet someone.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="n">click&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">echo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Hello, &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝後即可使用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">my-cli --name Python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># 輸出：Hello, Python!&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="常見問題與解決">常見問題與解決&lt;/h2>
&lt;h3 id="問題一相依性解析衝突">問題一：相依性解析衝突&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 錯誤訊息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">SolverProblemError: ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 解決方法&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 檢視衝突詳情&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">poetry show --tree
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 放寬版本限制&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">poetry add &lt;span class="s2">&amp;#34;package&amp;gt;=1.0,&amp;lt;3.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 強制更新 lock 檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">poetry lock --no-update&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="問題二虛擬環境問題">問題二：虛擬環境問題&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 重建虛擬環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">poetry env remove --all
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">poetry install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 指定 Python 版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">poetry env use /usr/bin/python3.11&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="問題三cicd-快取">問題三：CI/CD 快取&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># GitHub Actions 範例&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install Poetry&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">snok/install-poetry@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">virtualenvs-create&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">virtualenvs-in-project&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Load cached venv&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/cache@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.venv&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">key&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">venv-${{ runner.os }}-${{ hashFiles(&amp;#39;**/poetry.lock&amp;#39;) }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install dependencies&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">steps.cache.outputs.cache-hit != &amp;#39;true&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">poetry install --no-interaction&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="設計權衡">設計權衡&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>優點&lt;/th>
 &lt;th>缺點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>相依性鎖定&lt;/strong>&lt;/td>
 &lt;td>環境可重現、團隊一致&lt;/td>
 &lt;td>lock 檔案衝突、更新需謹慎&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>一體化工具&lt;/strong>&lt;/td>
 &lt;td>學習成本低、工作流統一&lt;/td>
 &lt;td>與其他工具整合需調整&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>虛擬環境整合&lt;/strong>&lt;/td>
 &lt;td>自動管理、不易混淆&lt;/td>
 &lt;td>自訂環境位置需設定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>建構與發布&lt;/strong>&lt;/td>
 &lt;td>流程簡化&lt;/td>
 &lt;td>不支援 C 擴展&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="練習">練習&lt;/h2>
&lt;h3 id="基礎練習建立-poetry-專案">基礎練習：建立 Poetry 專案&lt;/h3>
&lt;p>&lt;strong>目標&lt;/strong>：使用 Poetry 建立一個簡單的專案&lt;/p></description><content:encoded><![CDATA[<p>本案例展示如何使用 Poetry 管理現代 Python 專案的完整生命週期，從專案建立到套件發布。</p>
<h2 id="先備知識">先備知識</h2>
<ul>
<li><a href="/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">建構系統比較</a></li>
<li>Python 虛擬環境基礎</li>
<li>套件相依性概念</li>
</ul>
<h2 id="問題背景">問題背景</h2>
<h3 id="現代專案的挑戰">現代專案的挑戰</h3>
<p>傳統的 Python 專案管理面臨以下問題：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">傳統工作流程的痛點：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 相依性管理
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">│   ├── requirements.txt 無法鎖定間接相依性
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">│   ├── pip freeze 產生的版本可能過於嚴格
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│   └── 開發與生產環境相依性混在一起
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── 虛擬環境
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│   ├── 需要手動建立和管理
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">│   ├── 不同專案的環境容易混淆
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│   └── 缺乏與專案的關聯性
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 建構與發布
</span></span><span class="line"><span class="ln">11</span><span class="cl">│   ├── setup.py 設定複雜
</span></span><span class="line"><span class="ln">12</span><span class="cl">│   ├── 發布流程需要多個工具
</span></span><span class="line"><span class="ln">13</span><span class="cl">│   └── 版本管理不一致
</span></span><span class="line"><span class="ln">14</span><span class="cl">└── 團隊協作
</span></span><span class="line"><span class="ln">15</span><span class="cl">    ├── 環境難以重現
</span></span><span class="line"><span class="ln">16</span><span class="cl">    ├── 「在我電腦上可以跑」問題
</span></span><span class="line"><span class="ln">17</span><span class="cl">    └── 新成員上手困難</span></span></code></pre></div><h3 id="為什麼選擇-poetry">為什麼選擇 Poetry？</h3>
<table>
  <thead>
      <tr>
          <th>特性</th>
          <th>pip + venv</th>
          <th>Poetry</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>相依性鎖定</td>
          <td>手動（pip freeze）</td>
          <td>自動（poetry.lock）</td>
      </tr>
      <tr>
          <td>間接相依性追蹤</td>
          <td>無</td>
          <td>完整追蹤</td>
      </tr>
      <tr>
          <td>虛擬環境管理</td>
          <td>手動</td>
          <td>自動整合</td>
      </tr>
      <tr>
          <td>建構系統</td>
          <td>需要額外工具</td>
          <td>內建</td>
      </tr>
      <tr>
          <td>發布流程</td>
          <td>需要 twine</td>
          <td>內建</td>
      </tr>
      <tr>
          <td>相依性群組</td>
          <td>無原生支援</td>
          <td>完整支援</td>
      </tr>
  </tbody>
</table>
<h2 id="解決方案">解決方案</h2>
<h3 id="安裝-poetry">安裝 Poetry</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 官方推薦的安裝方式（獨立安裝）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">curl -sSL https://install.python-poetry.org <span class="p">|</span> python3 -
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 或使用 pipx（推薦用於 CLI 工具）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">pipx install poetry
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 確認安裝</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">poetry --version</span></span></code></pre></div><h4 id="設定-poetry">設定 Poetry</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 在專案目錄中建立虛擬環境（推薦）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">poetry config virtualenvs.in-project <span class="nb">true</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 查看所有設定</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">poetry config --list</span></span></code></pre></div><h3 id="工作流一建立新專案">工作流一：建立新專案</h3>
<h4 id="使用-poetry-new完整專案結構">使用 poetry new（完整專案結構）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 建立新專案</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry new my-awesome-lib
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 產生的目錄結構</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">my-awesome-lib/
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── my_awesome_lib/
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│   └── __init__.py
</span></span><span class="line"><span class="ln">10</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln">11</span><span class="cl">    └── __init__.py</span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 使用 src layout（推薦用於函式庫）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry new --src my-awesome-lib
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 產生的目錄結構</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">my-awesome-lib/
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│   └── my_awesome_lib/
</span></span><span class="line"><span class="ln">10</span><span class="cl">│       └── __init__.py
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln">12</span><span class="cl">    └── __init__.py</span></span></code></pre></div><h4 id="使用-poetry-init現有專案">使用 poetry init（現有專案）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 在現有目錄初始化</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nb">cd</span> existing-project
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">poetry init
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 互動式問答</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># - Package name</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># - Version</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># - Description</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># - Author</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># - License</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># - Python version</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># - Dependencies</span></span></span></code></pre></div><h5 id="互動式初始化範例">互動式初始化範例</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">This command will guide you through creating your pyproject.toml config.
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">Package name [existing-project]:  my-package
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">Version [0.1.0]:  1.0.0
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">Description []:  A useful Python package
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Author [Your Name &lt;you@example.com&gt;, n to skip]:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">License []:  MIT
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">Compatible Python versions [^3.10]:  ^3.10
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">Would you like to define your main dependencies interactively? (yes/no) [yes]
</span></span><span class="line"><span class="ln">11</span><span class="cl">Search for package to add (or leave blank to continue): requests
</span></span><span class="line"><span class="ln">12</span><span class="cl">...</span></span></code></pre></div><h3 id="工作流二管理相依性">工作流二：管理相依性</h3>
<h4 id="新增相依性">新增相依性</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 新增生產相依性</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry add requests
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">poetry add <span class="s2">&#34;httpx&gt;=0.24&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 新增開發相依性</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">poetry add pytest --group dev
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">poetry add ruff mypy --group dev
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 新增文件相依性</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">poetry add mkdocs mkdocs-material --group docs
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 新增可選相依性（extras）</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">poetry add pyyaml --optional</span></span></code></pre></div><h5 id="pyprojecttoml-的變化">pyproject.toml 的變化</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">python</span> <span class="p">=</span> <span class="s2">&#34;^3.10&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requests</span> <span class="p">=</span> <span class="s2">&#34;^2.31.0&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">httpx</span> <span class="p">=</span> <span class="s2">&#34;&gt;=0.24&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">dev</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">pytest</span> <span class="p">=</span> <span class="s2">&#34;^8.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">ruff</span> <span class="p">=</span> <span class="s2">&#34;^0.4.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">mypy</span> <span class="p">=</span> <span class="s2">&#34;^1.10.0&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">docs</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">mkdocs</span> <span class="p">=</span> <span class="s2">&#34;^1.5.0&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">mkdocs-material</span> <span class="p">=</span> <span class="s2">&#34;^9.5.0&#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">extras</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">yaml</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pyyaml&#34;</span><span class="p">]</span></span></span></code></pre></div><h4 id="移除相依性">移除相依性</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 移除生產相依性</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">poetry remove requests
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 移除開發相依性</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">poetry remove pytest --group dev</span></span></code></pre></div><h4 id="更新相依性">更新相依性</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 更新所有相依性</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry update
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 更新特定套件</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">poetry update requests
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 檢視可更新的套件</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">poetry show --outdated
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 檢視相依性樹</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">poetry show --tree</span></span></code></pre></div><h5 id="相依性樹範例輸出">相依性樹範例輸出</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">requests 2.31.0 Python HTTP for Humans.
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── certifi &gt;=2017.4.17
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── charset-normalizer &gt;=2,&lt;4
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── idna &gt;=2.5,&lt;4
</span></span><span class="line"><span class="ln">5</span><span class="cl">└── urllib3 &gt;=1.21.1,&lt;3</span></span></code></pre></div><h3 id="工作流三虛擬環境管理">工作流三：虛擬環境管理</h3>
<h4 id="自動環境管理">自動環境管理</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝所有相依性（自動建立虛擬環境）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry install
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 僅安裝生產相依性</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">poetry install --only main
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 安裝包含特定群組</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">poetry install --with dev,docs
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 排除特定群組</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">poetry install --without docs
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 安裝 extras</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">poetry install --extras yaml
</span></span><span class="line"><span class="ln">15</span><span class="cl">poetry install --all-extras</span></span></code></pre></div><h4 id="環境操作">環境操作</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 進入虛擬環境 shell</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry shell
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 在虛擬環境中執行命令（不進入 shell）</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">poetry run python script.py
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">poetry run pytest
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">poetry run python -c <span class="s2">&#34;import my_package; print(my_package.__version__)&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 顯示環境資訊</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">poetry env info
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 顯示環境路徑</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">poetry env info --path
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># 列出所有環境</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">poetry env list
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># 切換 Python 版本</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">poetry env use python3.11
</span></span><span class="line"><span class="ln">20</span><span class="cl">poetry env use 3.12
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"># 刪除環境</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">poetry env remove python3.11
</span></span><span class="line"><span class="ln">24</span><span class="cl">poetry env remove --all</span></span></code></pre></div><h5 id="環境資訊範例輸出">環境資訊範例輸出</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">Virtualenv
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">Python:         3.11.6
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">Implementation: CPython
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">Path:           /path/to/project/.venv
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">Executable:     /path/to/project/.venv/bin/python
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Valid:          True
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">Base
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Platform:   darwin
</span></span><span class="line"><span class="ln">10</span><span class="cl">OS:         posix
</span></span><span class="line"><span class="ln">11</span><span class="cl">Python:     3.11.6
</span></span><span class="line"><span class="ln">12</span><span class="cl">Path:       /opt/homebrew/Cellar/python@3.11/3.11.6/Frameworks/Python.framework/Versions/3.11
</span></span><span class="line"><span class="ln">13</span><span class="cl">Executable: /opt/homebrew/Cellar/python@3.11/3.11.6/Frameworks/Python.framework/Versions/3.11/bin/python3.11</span></span></code></pre></div><h3 id="工作流四建構與發布">工作流四：建構與發布</h3>
<h4 id="建構套件">建構套件</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 建構 sdist 和 wheel</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry build
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 僅建構 wheel</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">poetry build --format wheel
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 建構結果</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">dist/
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── my_package-1.0.0-py3-none-any.whl
</span></span><span class="line"><span class="ln">10</span><span class="cl">└── my_package-1.0.0.tar.gz</span></span></code></pre></div><h4 id="發布到-pypi">發布到 PyPI</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 設定 PyPI token</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry config pypi-token.pypi pypi-XXXXXXXXXXXX
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 發布到 PyPI</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">poetry publish
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 建構並發布（一步完成）</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">poetry publish --build
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 發布到 TestPyPI</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">poetry config repositories.testpypi https://test.pypi.org/legacy/
</span></span><span class="line"><span class="ln">12</span><span class="cl">poetry config pypi-token.testpypi pypi-XXXXXXXXXXXX
</span></span><span class="line"><span class="ln">13</span><span class="cl">poetry publish --repository testpypi</span></span></code></pre></div><h5 id="版本管理">版本管理</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 查看當前版本</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry version
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 升級版本</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">poetry version patch   <span class="c1"># 1.0.0 -&gt; 1.0.1</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">poetry version minor   <span class="c1"># 1.0.0 -&gt; 1.1.0</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">poetry version major   <span class="c1"># 1.0.0 -&gt; 2.0.0</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 設定特定版本</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">poetry version 2.0.0
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 預發布版本</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">poetry version prepatch  <span class="c1"># 1.0.0 -&gt; 1.0.1a0</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">poetry version preminor  <span class="c1"># 1.0.0 -&gt; 1.1.0a0</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">poetry version premajor  <span class="c1"># 1.0.0 -&gt; 2.0.0a0</span></span></span></code></pre></div><h3 id="完整-pyprojecttoml-範例">完整 pyproject.toml 範例</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;poetry-core&gt;=2.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;poetry.core.masonry.api&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-awesome-lib&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;A feature-rich Python library&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.10&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">{</span> <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;you@example.com&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">keywords</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;python&#34;</span><span class="p">,</span> <span class="s2">&#34;library&#34;</span><span class="p">,</span> <span class="s2">&#34;utilities&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">classifiers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="s2">&#34;Development Status :: 4 - Beta&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="s2">&#34;Intended Audience :: Developers&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="s2">&#34;License :: OSI Approved :: MIT License&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="s2">&#34;Operating System :: OS Independent&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.11&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.12&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: 3.13&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="s2">&#34;Typing :: Typed&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">urls</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">Homepage</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/my-awesome-lib&#34;</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="nx">Documentation</span> <span class="p">=</span> <span class="s2">&#34;https://my-awesome-lib.readthedocs.io&#34;</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="nx">Repository</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/my-awesome-lib.git&#34;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="nx">Changelog</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/my-awesome-lib/blob/main/CHANGELOG.md&#34;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="nx">my-cli</span> <span class="p">=</span> <span class="s2">&#34;my_awesome_lib.cli:main&#34;</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="nx">yaml</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pyyaml&gt;=6.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;my-awesome-lib[yaml]&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c"># ===== Poetry 特定設定 =====</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[{</span> <span class="nx">include</span> <span class="p">=</span> <span class="s2">&#34;my_awesome_lib&#34;</span><span class="p">,</span> <span class="nx">from</span> <span class="p">=</span> <span class="s2">&#34;src&#34;</span> <span class="p">}]</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="nx">python</span> <span class="p">=</span> <span class="s2">&#34;^3.10&#34;</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="nx">requests</span> <span class="p">=</span> <span class="s2">&#34;^2.31&#34;</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="nx">click</span> <span class="p">=</span> <span class="s2">&#34;^8.1&#34;</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">
</span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">dev</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="nx">pytest</span> <span class="p">=</span> <span class="s2">&#34;^8.0&#34;</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="nx">pytest-cov</span> <span class="p">=</span> <span class="s2">&#34;^4.1&#34;</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="nx">pytest-asyncio</span> <span class="p">=</span> <span class="s2">&#34;^0.23&#34;</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="nx">mypy</span> <span class="p">=</span> <span class="s2">&#34;^1.10&#34;</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="nx">ruff</span> <span class="p">=</span> <span class="s2">&#34;^0.4&#34;</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">
</span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">docs</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="nx">optional</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">
</span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">docs</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="nx">mkdocs</span> <span class="p">=</span> <span class="s2">&#34;^1.5&#34;</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="nx">mkdocs-material</span> <span class="p">=</span> <span class="s2">&#34;^9.5&#34;</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">
</span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="c"># ===== 工具設定 =====</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl">
</span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="nx">src</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="nx">line-length</span> <span class="p">=</span> <span class="mi">88</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="nx">target-version</span> <span class="p">=</span> <span class="s2">&#34;py310&#34;</span>
</span></span><span class="line"><span class="ln">72</span><span class="cl">
</span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="nx">select</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;E&#34;</span><span class="p">,</span> <span class="s2">&#34;W&#34;</span><span class="p">,</span> <span class="s2">&#34;F&#34;</span><span class="p">,</span> <span class="s2">&#34;I&#34;</span><span class="p">,</span> <span class="s2">&#34;B&#34;</span><span class="p">,</span> <span class="s2">&#34;C4&#34;</span><span class="p">,</span> <span class="s2">&#34;UP&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">75</span><span class="cl">
</span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">mypy</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="nx">python_version</span> <span class="p">=</span> <span class="s2">&#34;3.10&#34;</span>
</span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="nx">strict</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">79</span><span class="cl">
</span></span><span class="line"><span class="ln">80</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">pytest</span><span class="p">.</span><span class="nx">ini_options</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="nx">testpaths</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;tests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="nx">addopts</span> <span class="p">=</span> <span class="s2">&#34;-v --tb=short&#34;</span></span></span></code></pre></div><h2 id="與其他工具的比較">與其他工具的比較</h2>
<h3 id="poetry-vs-pip">Poetry vs pip</h3>
<table>
  <thead>
      <tr>
          <th>操作</th>
          <th>pip</th>
          <th>Poetry</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>安裝相依性</td>
          <td><code>pip install -r requirements.txt</code></td>
          <td><code>poetry install</code></td>
      </tr>
      <tr>
          <td>新增相依性</td>
          <td>手動編輯 requirements.txt</td>
          <td><code>poetry add package</code></td>
      </tr>
      <tr>
          <td>鎖定版本</td>
          <td><code>pip freeze &gt; requirements.txt</code></td>
          <td>自動更新 poetry.lock</td>
      </tr>
      <tr>
          <td>建立環境</td>
          <td><code>python -m venv .venv</code></td>
          <td>自動建立</td>
      </tr>
      <tr>
          <td>執行命令</td>
          <td><code>source .venv/bin/activate &amp;&amp; python</code></td>
          <td><code>poetry run python</code></td>
      </tr>
      <tr>
          <td>建構套件</td>
          <td><code>python -m build</code></td>
          <td><code>poetry build</code></td>
      </tr>
      <tr>
          <td>發布套件</td>
          <td><code>twine upload dist/*</code></td>
          <td><code>poetry publish</code></td>
      </tr>
  </tbody>
</table>
<h3 id="poetry-vs-setuptools">Poetry vs setuptools</h3>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>setuptools</th>
          <th>Poetry</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>設定格式</td>
          <td>pyproject.toml + 可能需要 setup.py</td>
          <td>僅 pyproject.toml</td>
      </tr>
      <tr>
          <td>相依性管理</td>
          <td>需要額外工具</td>
          <td>內建完整支援</td>
      </tr>
      <tr>
          <td>環境管理</td>
          <td>無</td>
          <td>內建</td>
      </tr>
      <tr>
          <td>學習曲線</td>
          <td>較陡（歷史包袱）</td>
          <td>較平緩</td>
      </tr>
      <tr>
          <td>C 擴展支援</td>
          <td>完整</td>
          <td>不支援</td>
      </tr>
      <tr>
          <td>生態系統</td>
          <td>最廣泛</td>
          <td>持續成長</td>
      </tr>
  </tbody>
</table>
<h3 id="poetry-vs-hatch">Poetry vs Hatch</h3>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Hatch</th>
          <th>Poetry</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>設計理念</td>
          <td>PEP 標準優先</td>
          <td>使用者體驗優先</td>
      </tr>
      <tr>
          <td>相依性鎖定</td>
          <td>無內建</td>
          <td>核心功能</td>
      </tr>
      <tr>
          <td>環境管理</td>
          <td>多環境（類似 tox）</td>
          <td>單一虛擬環境</td>
      </tr>
      <tr>
          <td>腳本系統</td>
          <td>完整</td>
          <td>基本</td>
      </tr>
      <tr>
          <td>建構後端</td>
          <td>hatchling</td>
          <td>poetry-core</td>
      </tr>
      <tr>
          <td>適用場景</td>
          <td>開源函式庫</td>
          <td>應用程式、內部工具</td>
      </tr>
  </tbody>
</table>
<h2 id="實用技巧">實用技巧</h2>
<h3 id="技巧一善用-lock-檔案">技巧一：善用 Lock 檔案</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># poetry.lock 的重要性</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"># - 記錄所有相依性的精確版本（包含間接相依性）</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># - 確保團隊成員、CI/CD 使用相同版本</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># - 應該提交到版本控制</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 根據 lock 檔案安裝（不更新）</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">poetry install --no-update
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 驗證 lock 檔案與 pyproject.toml 一致</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">poetry check --lock
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 匯出為 requirements.txt（部署用）</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">poetry <span class="nb">export</span> -f requirements.txt -o requirements.txt
</span></span><span class="line"><span class="ln">14</span><span class="cl">poetry <span class="nb">export</span> -f requirements.txt --with dev -o requirements-dev.txt
</span></span><span class="line"><span class="ln">15</span><span class="cl">poetry <span class="nb">export</span> --without-hashes -o requirements.txt</span></span></code></pre></div><h3 id="技巧二善用相依性群組">技巧二：善用相依性群組</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 開發相依性</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">dev</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">pytest</span> <span class="p">=</span> <span class="s2">&#34;^8.0&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">ruff</span> <span class="p">=</span> <span class="s2">&#34;^0.4&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c"># 可選群組（預設不安裝）</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">docs</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">optional</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">docs</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">mkdocs</span> <span class="p">=</span> <span class="s2">&#34;^1.5&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c"># CI 專用群組</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">ci</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">optional</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">ci</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nx">pytest-cov</span> <span class="p">=</span> <span class="s2">&#34;^4.1&#34;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">codecov</span> <span class="p">=</span> <span class="s2">&#34;^2.1&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 安裝特定群組</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">poetry install --with docs
</span></span><span class="line"><span class="ln">3</span><span class="cl">poetry install --with ci
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># CI 環境中的安裝</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">poetry install --only main,ci</span></span></code></pre></div><h3 id="技巧三善用-extras">技巧三：善用 Extras</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c"># 功能性 extras</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">yaml</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pyyaml&gt;=6.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">async</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;httpx&gt;=0.24&#34;</span><span class="p">,</span> <span class="s2">&#34;aiofiles&gt;=23.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c"># 完整安裝</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;my-package[yaml,async]&#34;</span><span class="p">]</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 使用者安裝方式</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">pip install my-package           <span class="c1"># 基本功能</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">pip install <span class="s2">&#34;my-package[yaml]&#34;</span>   <span class="c1"># 包含 YAML 支援</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">pip install <span class="s2">&#34;my-package[all]&#34;</span>    <span class="c1"># 所有功能</span></span></span></code></pre></div><h3 id="技巧四本地相依性和-git-相依性">技巧四：本地相依性和 Git 相依性</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c"># 本地路徑相依性</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">my-local-lib</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;../my-local-lib&#34;</span><span class="p">,</span> <span class="nx">develop</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c"># Git 相依性</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">my-git-lib</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/repo.git&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nx">my-git-lib</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/repo.git&#34;</span><span class="p">,</span> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">&#34;develop&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="nx">my-git-lib</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/repo.git&#34;</span><span class="p">,</span> <span class="nx">tag</span> <span class="p">=</span> <span class="s2">&#34;v1.0.0&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="nx">my-git-lib</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">git</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/user/repo.git&#34;</span><span class="p">,</span> <span class="nx">rev</span> <span class="p">=</span> <span class="s2">&#34;abc123&#34;</span> <span class="p">}</span></span></span></code></pre></div><h3 id="技巧五平台特定相依性">技巧五：平台特定相依性</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c"># 僅 Windows</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">pywin32</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;^306&#34;</span><span class="p">,</span> <span class="nx">markers</span> <span class="p">=</span> <span class="s2">&#34;sys_platform == &#39;win32&#39;&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c"># 僅 Linux</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">uvloop</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;^0.19&#34;</span><span class="p">,</span> <span class="nx">markers</span> <span class="p">=</span> <span class="s2">&#34;sys_platform == &#39;linux&#39;&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c"># Python 版本限制</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="nx">typing-extensions</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;^4.0&#34;</span><span class="p">,</span> <span class="nx">python</span> <span class="p">=</span> <span class="s2">&#34;&lt;3.11&#34;</span> <span class="p">}</span></span></span></code></pre></div><h3 id="技巧六poetry-腳本">技巧六：Poetry 腳本</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">my-cli</span> <span class="p">=</span> <span class="s2">&#34;my_package.cli:main&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">my-tool</span> <span class="p">=</span> <span class="s2">&#34;my_package.tools:run&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># src/my_package/cli.py</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">click</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nd">@click.command</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nd">@click.option</span><span class="p">(</span><span class="s2">&#34;--name&#34;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s2">&#34;World&#34;</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s2">&#34;Name to greet&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Greet someone.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Hello, </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 安裝後即可使用</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">my-cli --name Python
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 輸出：Hello, Python!</span></span></span></code></pre></div><h2 id="常見問題與解決">常見問題與解決</h2>
<h3 id="問題一相依性解析衝突">問題一：相依性解析衝突</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 錯誤訊息</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">SolverProblemError: ...
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 解決方法</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 1. 檢視衝突詳情</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">poetry show --tree
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># 2. 放寬版本限制</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">poetry add <span class="s2">&#34;package&gt;=1.0,&lt;3.0&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 3. 強制更新 lock 檔案</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">poetry lock --no-update</span></span></code></pre></div><h3 id="問題二虛擬環境問題">問題二：虛擬環境問題</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 重建虛擬環境</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">poetry env remove --all
</span></span><span class="line"><span class="ln">3</span><span class="cl">poetry install
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 指定 Python 版本</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">poetry env use /usr/bin/python3.11</span></span></code></pre></div><h3 id="問題三cicd-快取">問題三：CI/CD 快取</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># GitHub Actions 範例</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install Poetry</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">snok/install-poetry@v1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">virtualenvs-create</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">virtualenvs-in-project</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Load cached venv</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/cache@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">.venv</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">venv-${{ runner.os }}-${{ hashFiles(&#39;**/poetry.lock&#39;) }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install dependencies</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">  </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.cache.outputs.cache-hit != &#39;true&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">poetry install --no-interaction</span></span></span></code></pre></div><h2 id="設計權衡">設計權衡</h2>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>優點</th>
          <th>缺點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>相依性鎖定</strong></td>
          <td>環境可重現、團隊一致</td>
          <td>lock 檔案衝突、更新需謹慎</td>
      </tr>
      <tr>
          <td><strong>一體化工具</strong></td>
          <td>學習成本低、工作流統一</td>
          <td>與其他工具整合需調整</td>
      </tr>
      <tr>
          <td><strong>虛擬環境整合</strong></td>
          <td>自動管理、不易混淆</td>
          <td>自訂環境位置需設定</td>
      </tr>
      <tr>
          <td><strong>建構與發布</strong></td>
          <td>流程簡化</td>
          <td>不支援 C 擴展</td>
      </tr>
  </tbody>
</table>
<h2 id="練習">練習</h2>
<h3 id="基礎練習建立-poetry-專案">基礎練習：建立 Poetry 專案</h3>
<p><strong>目標</strong>：使用 Poetry 建立一個簡單的專案</p>
<ol>
<li>使用 <code>poetry new --src my-utils</code> 建立專案</li>
<li>新增 <code>requests</code> 作為生產相依性</li>
<li>新增 <code>pytest</code> 和 <code>ruff</code> 作為開發相依性</li>
<li>執行 <code>poetry install</code> 並驗證環境</li>
</ol>
<h3 id="進階練習設定相依性群組">進階練習：設定相依性群組</h3>
<p><strong>目標</strong>：建立完整的相依性管理結構</p>
<ol>
<li>建立 <code>dev</code>、<code>docs</code>、<code>ci</code> 三個群組</li>
<li>將 <code>docs</code> 和 <code>ci</code> 設為可選群組</li>
<li>練習使用 <code>--with</code> 和 <code>--without</code> 選項</li>
</ol>
<h3 id="挑戰題完整發布流程">挑戰題：完整發布流程</h3>
<p><strong>目標</strong>：將專案發布到 TestPyPI</p>
<ol>
<li>設定 TestPyPI repository</li>
<li>使用 <code>poetry version</code> 管理版本</li>
<li>執行 <code>poetry build</code> 建構套件</li>
<li>執行 <code>poetry publish --repository testpypi</code> 發布</li>
</ol>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://python-poetry.org/docs/">Poetry 官方文件</a></li>
<li><a href="https://python-poetry.org/docs/cli/">Poetry CLI 參考</a></li>
<li><a href="https://peps.python.org/pep-0621/">pyproject.toml 規範 (PEP 621)</a></li>
<li><a href="https://packaging.python.org/">Python 打包使用者指南</a></li>
</ul>
<hr>
<p>返回：<a href="/blog/python-advanced/07-packaging/case-studies/" data-link-title="案例研究" data-link-desc="基於 .claude/lib 實際程式碼的打包發布案例">案例研究</a></p>
]]></content:encoded></item><item><title>6.2 建構系統比較</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/build-systems/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/build-systems/</guid><description>&lt;p>本章比較主流的 Python 套件建構系統。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>學完本章後，你將能夠：&lt;/p>
&lt;ol>
&lt;li>理解不同建構系統的設計理念&lt;/li>
&lt;li>根據專案需求選擇適合的工具&lt;/li>
&lt;li>在不同工具之間遷移&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="總覽建構系統生態">【總覽】建構系統生態&lt;/h2>
&lt;h3 id="建構後端-vs-建構前端">建構後端 vs 建構前端&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">建構前端（Frontend）：使用者互動的工具
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── pip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">└── 各工具的 CLI（poetry, hatch, etc.）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">建構後端（Backend）：實際執行建構的程式
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── setuptools.build_meta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── flit_core.buildapi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── hatchling.build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── poetry.core.masonry.api
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">└── maturin, scikit-build-core, mesonpy&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="主流工具比較">主流工具比較&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>工具&lt;/th>
 &lt;th>定位&lt;/th>
 &lt;th>特點&lt;/th>
 &lt;th>適用場景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>setuptools&lt;/td>
 &lt;td>建構後端&lt;/td>
 &lt;td>歷史最久，功能最全&lt;/td>
 &lt;td>一般專案、C 擴展&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Flit&lt;/td>
 &lt;td>建構後端&lt;/td>
 &lt;td>極簡設計&lt;/td>
 &lt;td>純 Python 小型專案&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Hatch&lt;/td>
 &lt;td>全套工具&lt;/td>
 &lt;td>現代設計，環境管理&lt;/td>
 &lt;td>新專案&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Poetry&lt;/td>
 &lt;td>全套工具&lt;/td>
 &lt;td>依賴鎖定，虛擬環境&lt;/td>
 &lt;td>應用程式開發&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PDM&lt;/td>
 &lt;td>全套工具&lt;/td>
 &lt;td>PEP 582，快速&lt;/td>
 &lt;td>實驗性專案&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="工具一setuptools">【工具一】setuptools&lt;/h2>
&lt;h3 id="特點與定位">特點與定位&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">setuptools：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── Python 打包的「標準」
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 歷史最悠久（2004 年開始）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 支援所有功能（C 擴展、資料檔案等）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 學習曲線較陡
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">└── PEP 621 支援（61.0.0+ 版本）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="基本設定">基本設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">packages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">where&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="進階設定">進階設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c"># 明確指定套件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;my_package.submodule&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c"># 包含資料檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">package-data&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;*.json&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;data/*&amp;#34;&lt;/span>&lt;span class="p">]}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c"># 排除檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">exclude-package-data&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;test_*&amp;#34;&lt;/span>&lt;span class="p">]}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c"># Zip 安全（是否可以作為 zip 檔案匯入）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">zip-safe&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dynamic&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">attr&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.__version__&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">file&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>&lt;span class="p">]}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="c-擴展支援">C 擴展支援&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;cython&amp;gt;=3.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># setup.py（仍需要用於複雜的 C 擴展）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">setuptools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">setup&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Extension&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">Cython.Build&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">cythonize&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="n">extensions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="n">Extension&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;my_package.fast_module&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src/my_package/fast_module.pyx&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="n">setup&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="n">ext_modules&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">cythonize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">extensions&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常用命令">常用命令&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">python -m build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 可編輯安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">pip install -e .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 開發模式（含額外依賴）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">pip install -e &lt;span class="s2">&amp;#34;.[dev]&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="工具二flit">【工具二】Flit&lt;/h2>
&lt;h3 id="特點與定位-1">特點與定位&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Flit：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── 極簡設計
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 只支援純 Python 套件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 不支援 C 擴展
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 快速建構
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">└── 適合簡單函式庫&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="基本設定-1">基本設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;flit_core&amp;gt;=3.4&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;flit_core.buildapi&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;A simple package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;you@example.com&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c"># Flit 自動從 __init__.py 讀取 docstring 和 version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c"># 所以常常不需要設定 description 和 version&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># src/my_package/__init__.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;A simple package for doing things.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="n">__version__&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常用命令-1">常用命令&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 flit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">pip install flit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">flit build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">flit publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 可編輯安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">flit install --symlink &lt;span class="c1"># Unix&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">flit install --pth-file &lt;span class="c1"># Windows&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="限制">限制&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Flit 不支援：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── C/C++/Rust 擴展
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 複雜的建構腳本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 資料檔案的細粒度控制
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 動態版本（需要在 __init__.py 中定義）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">└── 依賴鎖定&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="工具三hatch">【工具三】Hatch&lt;/h2>
&lt;h3 id="特點與定位-2">特點與定位&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Hatch：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── 現代化設計
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 環境管理（類似 tox）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 版本管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 腳本系統
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">├── PEP 標準優先
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">└── 由 PyPA 成員維護&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="基本設定-2">基本設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;hatchling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;hatchling.build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">targets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">wheel&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src/my_package&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">targets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sdist&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">include&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/src&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;/tests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="環境管理">環境管理&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># 定義環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pytest-cov&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">test&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">cov&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest --cov=my_package {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;ruff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;mypy&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">check&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;ruff check .&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;mypy src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="nx">fix&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;ruff check --fix .&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;sphinx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;sphinx-rtd-theme&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="nx">build&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;sphinx-build -b html docs docs/_build&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="版本管理">版本管理&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/my_package/__init__.py&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c"># 或使用 hatch-vcs 從 git 標籤讀取&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c"># [tool.hatch.version]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c"># source = &amp;#34;vcs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c"># [tool.hatch.build.hooks.vcs]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c"># version-file = &amp;#34;src/my_package/_version.py&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常用命令-2">常用命令&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 hatch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">pip install hatch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建立新專案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">hatch new my-package
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 執行腳本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">hatch run &lt;span class="nb">test&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">hatch run lint:check
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c1"># 進入環境 shell&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">hatch shell
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c1"># 版本管理&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">hatch version &lt;span class="c1"># 顯示當前版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">hatch version minor &lt;span class="c1"># 升級 minor 版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">hatch version 2.0.0 &lt;span class="c1"># 設定特定版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">hatch build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">hatch publish&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="工具四poetry">【工具四】Poetry&lt;/h2>
&lt;h3 id="特點與定位-3">特點與定位&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Poetry：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── 依賴解析與鎖定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 虛擬環境管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 發布流程整合
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 自己的設定格式（[tool.poetry]）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">├── Poetry 2.0 支援 [project] 表
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">└── 適合應用程式開發&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="基本設定poetry-20">基本設定（Poetry 2.0+）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml（Poetry 2.0 風格）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;poetry-core&amp;gt;=2.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;poetry.core.masonry.api&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;My awesome package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;you@example.com&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requests&amp;gt;=2.28&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;gt;=7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;ruff&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="c"># Poetry 特定設定仍在 [tool.poetry]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="nx">include&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^7.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="nx">ruff&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^0.1&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="傳統設定poetry-1x">傳統設定（Poetry 1.x）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml（舊風格，仍支援）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;My awesome package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Your Name &amp;lt;you@example.com&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">requests&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^2.28&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^7.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="nx">ruff&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^0.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;poetry-core&amp;gt;=1.0.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;poetry.core.masonry.api&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="依賴鎖定">依賴鎖定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># poetry.lock 檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1"># - 記錄所有依賴的精確版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c1"># - 確保團隊成員使用相同版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># - 應該提交到版本控制&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝（使用 lock 檔案）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">poetry install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># 更新依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">poetry update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># 新增依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">poetry add requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">poetry add pytest --group dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="c1"># 移除依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">poetry remove requests&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常用命令-3">常用命令&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 poetry&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">pip install poetry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或官方推薦的安裝方式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">curl -sSL https://install.python-poetry.org &lt;span class="p">|&lt;/span> python3 -
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建立新專案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">poetry new my-package
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">poetry init &lt;span class="c1"># 在現有目錄初始化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 虛擬環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">poetry env use python3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">poetry shell &lt;span class="c1"># 進入虛擬環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c1"># 執行命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">poetry run python script.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">poetry run pytest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構與發布&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">poetry build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">poetry publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="c1"># 設定 PyPI token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">poetry config pypi-token.pypi &amp;lt;your-token&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="工具五pdm">【工具五】PDM&lt;/h2>
&lt;h3 id="特點與定位-4">特點與定位&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">PDM：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── 支援 PEP 582（__pypackages__）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 快速的依賴解析
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 支援 PEP 621
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 插件系統
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">└── 實驗性功能多&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="基本設定-3">基本設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pdm-backend&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pdm.backend&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pdm&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">distribution&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pdm&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;ruff&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常用命令-4">常用命令&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 pdm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">pip install pdm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 初始化專案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">pdm init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 依賴管理&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">pdm add requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">pdm add -d pytest &lt;span class="c1"># 開發依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">pdm remove requests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">pdm update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># 執行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">pdm run python script.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">pdm run pytest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">pdm build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">pdm publish&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="選擇指南決策流程">【選擇指南】決策流程&lt;/h2>
&lt;h3 id="決策樹">決策樹&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">需要選擇建構系統？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 需要 C/Rust 擴展？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">│ ├── 是 → setuptools + Cython/pybind11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">│ │ 或 maturin（Rust）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">│ └── 否 ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── 需要依賴鎖定？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ ├── 是 → Poetry 或 PDM
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">│ └── 否 ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── 需要環境管理？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">│ ├── 是 → Hatch 或 Poetry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">│ └── 否 ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">├── 極簡專案？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">│ ├── 是 → Flit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">│ └── 否 → setuptools 或 Hatch&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="場景建議">場景建議&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">場景 推薦工具
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">─────────────────────────────────────────────────────
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">純 Python 函式庫 Flit 或 Hatch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">Web 應用程式 Poetry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">資料科學專案 Poetry 或 PDM
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">有 C 擴展的函式庫 setuptools
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">Rust 擴展 Maturin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">開源專案（多貢獻者） Hatch 或 setuptools
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">內部工具 Poetry&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="遷移考量">遷移考量&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">從 setup.py 遷移：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 純 Python → 任何工具都可以
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 有 C 擴展 → setuptools（保留部分 setup.py）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">└── 複雜建構 → 保持 setuptools
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">從 Poetry 1.x 遷移到 2.0：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── 更新 poetry-core 版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── 可選：將 [tool.poetry.dependencies] 移到 [project]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">└── 測試建構和安裝
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">工具之間遷移：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── 所有現代工具都支援 PEP 621
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">├── 主要差異在 [tool.xxx] 設定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">└── 依賴鎖定檔案不相容&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="比較功能對照表">【比較】功能對照表&lt;/h2>
&lt;h3 id="核心功能">核心功能&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>功能&lt;/th>
 &lt;th>setuptools&lt;/th>
 &lt;th>Flit&lt;/th>
 &lt;th>Hatch&lt;/th>
 &lt;th>Poetry&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>PEP 621&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援 (2.0)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>C 擴展&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>環境管理&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>依賴鎖定&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>腳本系統&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>版本管理&lt;/td>
 &lt;td>有限&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>插件系統&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="效能比較">效能比較&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">建構速度（純 Python 專案）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">Flit &amp;gt; Hatch &amp;gt; Poetry &amp;gt; setuptools
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">依賴解析速度：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">PDM &amp;gt; Poetry &amp;gt; pip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">安裝速度：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">pip + lock file &amp;gt; Poetry &amp;gt; PDM&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="思考題">思考題&lt;/h2>
&lt;ol>
&lt;li>為什麼 Python 社群有這麼多打包工具？這是好事還是壞事？&lt;/li>
&lt;li>依賴鎖定對函式庫和應用程式的重要性有什麼不同？&lt;/li>
&lt;li>如果要開始一個新的開源專案，你會選擇哪個工具？為什麼？&lt;/li>
&lt;/ol>
&lt;h2 id="實作練習">實作練習&lt;/h2>
&lt;ol>
&lt;li>用 setuptools、Flit、Hatch 三種工具建立相同的簡單套件，比較設定檔的差異&lt;/li>
&lt;li>使用 Poetry 建立一個有依賴鎖定的專案，模擬團隊協作場景&lt;/li>
&lt;li>將一個現有的 setup.py 專案遷移到 pyproject.toml&lt;/li>
&lt;/ol>
&lt;h2 id="延伸閱讀">延伸閱讀&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://packaging.python.org/">Python Packaging User Guide&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://hatch.pypa.io/">Hatch 官方文件&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://python-poetry.org/docs/">Poetry 官方文件&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://flit.pypa.io/">Flit 官方文件&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;em>上一章：&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/pyproject-toml/" data-link-title="6.1 pyproject.toml 完整指南" data-link-desc="理解現代 Python 套件的設定標準">pyproject.toml 完整指南&lt;/a>&lt;/em>
&lt;em>下一章：&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/distribution/" data-link-title="6.3 發布到 PyPI" data-link-desc="學習如何建構 wheel 並發布到 PyPI">發布到 PyPI&lt;/a>&lt;/em>&lt;/p></description><content:encoded><![CDATA[<p>本章比較主流的 Python 套件建構系統。</p>
<h2 id="本章目標">本章目標</h2>
<p>學完本章後，你將能夠：</p>
<ol>
<li>理解不同建構系統的設計理念</li>
<li>根據專案需求選擇適合的工具</li>
<li>在不同工具之間遷移</li>
</ol>
<hr>
<h2 id="總覽建構系統生態">【總覽】建構系統生態</h2>
<h3 id="建構後端-vs-建構前端">建構後端 vs 建構前端</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">建構前端（Frontend）：使用者互動的工具
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── pip
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── build
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">└── 各工具的 CLI（poetry, hatch, etc.）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">建構後端（Backend）：實際執行建構的程式
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── setuptools.build_meta
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── flit_core.buildapi
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── hatchling.build
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── poetry.core.masonry.api
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── maturin, scikit-build-core, mesonpy</span></span></code></pre></div><h3 id="主流工具比較">主流工具比較</h3>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>定位</th>
          <th>特點</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>setuptools</td>
          <td>建構後端</td>
          <td>歷史最久，功能最全</td>
          <td>一般專案、C 擴展</td>
      </tr>
      <tr>
          <td>Flit</td>
          <td>建構後端</td>
          <td>極簡設計</td>
          <td>純 Python 小型專案</td>
      </tr>
      <tr>
          <td>Hatch</td>
          <td>全套工具</td>
          <td>現代設計，環境管理</td>
          <td>新專案</td>
      </tr>
      <tr>
          <td>Poetry</td>
          <td>全套工具</td>
          <td>依賴鎖定，虛擬環境</td>
          <td>應用程式開發</td>
      </tr>
      <tr>
          <td>PDM</td>
          <td>全套工具</td>
          <td>PEP 582，快速</td>
          <td>實驗性專案</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="工具一setuptools">【工具一】setuptools</h2>
<h3 id="特點與定位">特點與定位</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">setuptools：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── Python 打包的「標準」
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 歷史最悠久（2004 年開始）
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 支援所有功能（C 擴展、資料檔案等）
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 學習曲線較陡
</span></span><span class="line"><span class="ln">6</span><span class="cl">└── PEP 621 支援（61.0.0+ 版本）</span></span></code></pre></div><h3 id="基本設定">基本設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">packages</span><span class="p">.</span><span class="nx">find</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">where</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="進階設定">進階設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># 明確指定套件</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;my_package&#34;</span><span class="p">,</span> <span class="s2">&#34;my_package.submodule&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c"># 包含資料檔案</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">package-data</span> <span class="p">=</span> <span class="p">{</span><span class="s2">&#34;my_package&#34;</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;*.json&#34;</span><span class="p">,</span> <span class="s2">&#34;data/*&#34;</span><span class="p">]}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c"># 排除檔案</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">exclude-package-data</span> <span class="p">=</span> <span class="p">{</span><span class="s2">&#34;my_package&#34;</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;test_*&#34;</span><span class="p">]}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c"># Zip 安全（是否可以作為 zip 檔案匯入）</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">zip-safe</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools</span><span class="p">.</span><span class="nx">dynamic</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="p">{</span><span class="nx">attr</span> <span class="p">=</span> <span class="s2">&#34;my_package.__version__&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="p">{</span><span class="nx">file</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;README.md&#34;</span><span class="p">]}</span></span></span></code></pre></div><h3 id="c-擴展支援">C 擴展支援</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">,</span> <span class="s2">&#34;cython&gt;=3.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># setup.py（仍需要用於複雜的 C 擴展）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">from</span> <span class="nn">setuptools</span> <span class="kn">import</span> <span class="n">setup</span><span class="p">,</span> <span class="n">Extension</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">from</span> <span class="nn">Cython.Build</span> <span class="kn">import</span> <span class="n">cythonize</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">extensions</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">Extension</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="s2">&#34;my_package.fast_module&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="p">[</span><span class="s2">&#34;src/my_package/fast_module.pyx&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">setup</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">ext_modules</span><span class="o">=</span><span class="n">cythonize</span><span class="p">(</span><span class="n">extensions</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">)</span></span></span></code></pre></div><h3 id="常用命令">常用命令</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 建構</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">python -m build
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 可編輯安裝</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">pip install -e .
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 開發模式（含額外依賴）</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">pip install -e <span class="s2">&#34;.[dev]&#34;</span></span></span></code></pre></div><hr>
<h2 id="工具二flit">【工具二】Flit</h2>
<h3 id="特點與定位-1">特點與定位</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Flit：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 極簡設計
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 只支援純 Python 套件
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 不支援 C 擴展
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 快速建構
</span></span><span class="line"><span class="ln">6</span><span class="cl">└── 適合簡單函式庫</span></span></code></pre></div><h3 id="基本設定-1">基本設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;flit_core&gt;=3.4&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;flit_core.buildapi&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;A simple package&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[{</span><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;you@example.com&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c"># Flit 自動從 __init__.py 讀取 docstring 和 version</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c"># 所以常常不需要設定 description 和 version</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># src/my_package/__init__.py</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="s2">&#34;&#34;&#34;A simple package for doing things.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">__version__</span> <span class="o">=</span> <span class="s2">&#34;1.0.0&#34;</span></span></span></code></pre></div><h3 id="常用命令-1">常用命令</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝 flit</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install flit
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 建構</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">flit build
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 發布</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">flit publish
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 可編輯安裝</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">flit install --symlink  <span class="c1"># Unix</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">flit install --pth-file <span class="c1"># Windows</span></span></span></code></pre></div><h3 id="限制">限制</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Flit 不支援：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── C/C++/Rust 擴展
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 複雜的建構腳本
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 資料檔案的細粒度控制
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 動態版本（需要在 __init__.py 中定義）
</span></span><span class="line"><span class="ln">6</span><span class="cl">└── 依賴鎖定</span></span></code></pre></div><hr>
<h2 id="工具三hatch">【工具三】Hatch</h2>
<h3 id="特點與定位-2">特點與定位</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Hatch：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 現代化設計
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 環境管理（類似 tox）
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 版本管理
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 腳本系統
</span></span><span class="line"><span class="ln">6</span><span class="cl">├── PEP 標準優先
</span></span><span class="line"><span class="ln">7</span><span class="cl">└── 由 PyPA 成員維護</span></span></code></pre></div><h3 id="基本設定-2">基本設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/my_package&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">sdist</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">include</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;/src&#34;</span><span class="p">,</span> <span class="s2">&#34;/tests&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="環境管理">環境管理</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 定義環境</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&#34;</span><span class="p">,</span> <span class="s2">&#34;pytest-cov&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">test</span> <span class="p">=</span> <span class="s2">&#34;pytest {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">cov</span> <span class="p">=</span> <span class="s2">&#34;pytest --cov=my_package {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;ruff&#34;</span><span class="p">,</span> <span class="s2">&#34;mypy&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">lint</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">check</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;ruff check .&#34;</span><span class="p">,</span> <span class="s2">&#34;mypy src&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">fix</span> <span class="p">=</span> <span class="s2">&#34;ruff check --fix .&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">docs</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;sphinx&#34;</span><span class="p">,</span> <span class="s2">&#34;sphinx-rtd-theme&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">docs</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nx">build</span> <span class="p">=</span> <span class="s2">&#34;sphinx-build -b html docs docs/_build&#34;</span></span></span></code></pre></div><h3 id="版本管理">版本管理</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;src/my_package/__init__.py&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c"># 或使用 hatch-vcs 從 git 標籤讀取</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c"># [tool.hatch.version]</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c"># source = &#34;vcs&#34;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c">#</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c"># [tool.hatch.build.hooks.vcs]</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c"># version-file = &#34;src/my_package/_version.py&#34;</span></span></span></code></pre></div><h3 id="常用命令-2">常用命令</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝 hatch</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install hatch
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 建立新專案</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">hatch new my-package
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 執行腳本</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">hatch run <span class="nb">test</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">hatch run lint:check
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 進入環境 shell</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">hatch shell
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># 版本管理</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">hatch version        <span class="c1"># 顯示當前版本</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">hatch version minor  <span class="c1"># 升級 minor 版本</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">hatch version 2.0.0  <span class="c1"># 設定特定版本</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"># 建構</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">hatch build
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"># 發布</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">hatch publish</span></span></code></pre></div><hr>
<h2 id="工具四poetry">【工具四】Poetry</h2>
<h3 id="特點與定位-3">特點與定位</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Poetry：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 依賴解析與鎖定
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 虛擬環境管理
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 發布流程整合
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 自己的設定格式（[tool.poetry]）
</span></span><span class="line"><span class="ln">6</span><span class="cl">├── Poetry 2.0 支援 [project] 表
</span></span><span class="line"><span class="ln">7</span><span class="cl">└── 適合應用程式開發</span></span></code></pre></div><h3 id="基本設定poetry-20">基本設定（Poetry 2.0+）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml（Poetry 2.0 風格）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;poetry-core&gt;=2.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;poetry.core.masonry.api&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;My awesome package&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[{</span><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;you@example.com&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&gt;=2.28&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.8&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&gt;=7.0&#34;</span><span class="p">,</span> <span class="s2">&#34;ruff&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c"># Poetry 特定設定仍在 [tool.poetry]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[{</span><span class="nx">include</span> <span class="p">=</span> <span class="s2">&#34;my_package&#34;</span><span class="p">,</span> <span class="nx">from</span> <span class="p">=</span> <span class="s2">&#34;src&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">dev</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">pytest</span> <span class="p">=</span> <span class="s2">&#34;^7.0&#34;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nx">ruff</span> <span class="p">=</span> <span class="s2">&#34;^0.1&#34;</span></span></span></code></pre></div><h3 id="傳統設定poetry-1x">傳統設定（Poetry 1.x）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml（舊風格，仍支援）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;My awesome package&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;Your Name &lt;you@example.com&gt;&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">python</span> <span class="p">=</span> <span class="s2">&#34;^3.8&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">requests</span> <span class="p">=</span> <span class="s2">&#34;^2.28&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">dev</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">pytest</span> <span class="p">=</span> <span class="s2">&#34;^7.0&#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">ruff</span> <span class="p">=</span> <span class="s2">&#34;^0.1&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;poetry-core&gt;=1.0.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;poetry.core.masonry.api&#34;</span></span></span></code></pre></div><h3 id="依賴鎖定">依賴鎖定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># poetry.lock 檔案</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"># - 記錄所有依賴的精確版本</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># - 確保團隊成員使用相同版本</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># - 應該提交到版本控制</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 安裝（使用 lock 檔案）</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">poetry install
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 更新依賴</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">poetry update
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 新增依賴</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">poetry add requests
</span></span><span class="line"><span class="ln">14</span><span class="cl">poetry add pytest --group dev
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># 移除依賴</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">poetry remove requests</span></span></code></pre></div><h3 id="常用命令-3">常用命令</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝 poetry</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install poetry
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># 或官方推薦的安裝方式</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">curl -sSL https://install.python-poetry.org <span class="p">|</span> python3 -
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 建立新專案</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">poetry new my-package
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">poetry init  <span class="c1"># 在現有目錄初始化</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 虛擬環境</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">poetry env use python3.11
</span></span><span class="line"><span class="ln">12</span><span class="cl">poetry shell  <span class="c1"># 進入虛擬環境</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># 執行命令</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">poetry run python script.py
</span></span><span class="line"><span class="ln">16</span><span class="cl">poetry run pytest
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># 建構與發布</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">poetry build
</span></span><span class="line"><span class="ln">20</span><span class="cl">poetry publish
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"># 設定 PyPI token</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">poetry config pypi-token.pypi &lt;your-token&gt;</span></span></code></pre></div><hr>
<h2 id="工具五pdm">【工具五】PDM</h2>
<h3 id="特點與定位-4">特點與定位</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">PDM：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 支援 PEP 582（__pypackages__）
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 快速的依賴解析
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 支援 PEP 621
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 插件系統
</span></span><span class="line"><span class="ln">6</span><span class="cl">└── 實驗性功能多</span></span></code></pre></div><h3 id="基本設定-3">基本設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pdm-backend&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;pdm.backend&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">pdm</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">distribution</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">pdm</span><span class="p">.</span><span class="nx">dev-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&#34;</span><span class="p">,</span> <span class="s2">&#34;ruff&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="常用命令-4">常用命令</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝 pdm</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install pdm
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 初始化專案</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">pdm init
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 依賴管理</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">pdm add requests
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">pdm add -d pytest  <span class="c1"># 開發依賴</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">pdm remove requests
</span></span><span class="line"><span class="ln">11</span><span class="cl">pdm update
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 執行</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">pdm run python script.py
</span></span><span class="line"><span class="ln">15</span><span class="cl">pdm run pytest
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"># 建構</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">pdm build
</span></span><span class="line"><span class="ln">19</span><span class="cl">pdm publish</span></span></code></pre></div><hr>
<h2 id="選擇指南決策流程">【選擇指南】決策流程</h2>
<h3 id="決策樹">決策樹</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">需要選擇建構系統？
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">│
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 需要 C/Rust 擴展？
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">│   ├── 是 → setuptools + Cython/pybind11
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│   │        或 maturin（Rust）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">│   └── 否 ↓
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 需要依賴鎖定？
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│   ├── 是 → Poetry 或 PDM
</span></span><span class="line"><span class="ln">10</span><span class="cl">│   └── 否 ↓
</span></span><span class="line"><span class="ln">11</span><span class="cl">│
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── 需要環境管理？
</span></span><span class="line"><span class="ln">13</span><span class="cl">│   ├── 是 → Hatch 或 Poetry
</span></span><span class="line"><span class="ln">14</span><span class="cl">│   └── 否 ↓
</span></span><span class="line"><span class="ln">15</span><span class="cl">│
</span></span><span class="line"><span class="ln">16</span><span class="cl">├── 極簡專案？
</span></span><span class="line"><span class="ln">17</span><span class="cl">│   ├── 是 → Flit
</span></span><span class="line"><span class="ln">18</span><span class="cl">│   └── 否 → setuptools 或 Hatch</span></span></code></pre></div><h3 id="場景建議">場景建議</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">場景                         推薦工具
</span></span><span class="line"><span class="ln">2</span><span class="cl">─────────────────────────────────────────────────────
</span></span><span class="line"><span class="ln">3</span><span class="cl">純 Python 函式庫             Flit 或 Hatch
</span></span><span class="line"><span class="ln">4</span><span class="cl">Web 應用程式                 Poetry
</span></span><span class="line"><span class="ln">5</span><span class="cl">資料科學專案                 Poetry 或 PDM
</span></span><span class="line"><span class="ln">6</span><span class="cl">有 C 擴展的函式庫            setuptools
</span></span><span class="line"><span class="ln">7</span><span class="cl">Rust 擴展                    Maturin
</span></span><span class="line"><span class="ln">8</span><span class="cl">開源專案（多貢獻者）         Hatch 或 setuptools
</span></span><span class="line"><span class="ln">9</span><span class="cl">內部工具                     Poetry</span></span></code></pre></div><h3 id="遷移考量">遷移考量</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">從 setup.py 遷移：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 純 Python → 任何工具都可以
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 有 C 擴展 → setuptools（保留部分 setup.py）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">└── 複雜建構 → 保持 setuptools
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">從 Poetry 1.x 遷移到 2.0：
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── 更新 poetry-core 版本
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 可選：將 [tool.poetry.dependencies] 移到 [project]
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">└── 測試建構和安裝
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">工具之間遷移：
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── 所有現代工具都支援 PEP 621
</span></span><span class="line"><span class="ln">13</span><span class="cl">├── 主要差異在 [tool.xxx] 設定
</span></span><span class="line"><span class="ln">14</span><span class="cl">└── 依賴鎖定檔案不相容</span></span></code></pre></div><hr>
<h2 id="比較功能對照表">【比較】功能對照表</h2>
<h3 id="核心功能">核心功能</h3>
<table>
  <thead>
      <tr>
          <th>功能</th>
          <th>setuptools</th>
          <th>Flit</th>
          <th>Hatch</th>
          <th>Poetry</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>PEP 621</td>
          <td>支援</td>
          <td>支援</td>
          <td>支援</td>
          <td>支援 (2.0)</td>
      </tr>
      <tr>
          <td>C 擴展</td>
          <td>支援</td>
          <td>不支援</td>
          <td>不支援</td>
          <td>不支援</td>
      </tr>
      <tr>
          <td>環境管理</td>
          <td>不支援</td>
          <td>不支援</td>
          <td>支援</td>
          <td>支援</td>
      </tr>
      <tr>
          <td>依賴鎖定</td>
          <td>不支援</td>
          <td>不支援</td>
          <td>不支援</td>
          <td>支援</td>
      </tr>
      <tr>
          <td>腳本系統</td>
          <td>不支援</td>
          <td>不支援</td>
          <td>支援</td>
          <td>支援</td>
      </tr>
      <tr>
          <td>版本管理</td>
          <td>有限</td>
          <td>不支援</td>
          <td>支援</td>
          <td>支援</td>
      </tr>
      <tr>
          <td>插件系統</td>
          <td>支援</td>
          <td>不支援</td>
          <td>支援</td>
          <td>支援</td>
      </tr>
  </tbody>
</table>
<h3 id="效能比較">效能比較</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">建構速度（純 Python 專案）：
</span></span><span class="line"><span class="ln">2</span><span class="cl">Flit &gt; Hatch &gt; Poetry &gt; setuptools
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">依賴解析速度：
</span></span><span class="line"><span class="ln">5</span><span class="cl">PDM &gt; Poetry &gt; pip
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl">安裝速度：
</span></span><span class="line"><span class="ln">8</span><span class="cl">pip + lock file &gt; Poetry &gt; PDM</span></span></code></pre></div><hr>
<h2 id="思考題">思考題</h2>
<ol>
<li>為什麼 Python 社群有這麼多打包工具？這是好事還是壞事？</li>
<li>依賴鎖定對函式庫和應用程式的重要性有什麼不同？</li>
<li>如果要開始一個新的開源專案，你會選擇哪個工具？為什麼？</li>
</ol>
<h2 id="實作練習">實作練習</h2>
<ol>
<li>用 setuptools、Flit、Hatch 三種工具建立相同的簡單套件，比較設定檔的差異</li>
<li>使用 Poetry 建立一個有依賴鎖定的專案，模擬團隊協作場景</li>
<li>將一個現有的 setup.py 專案遷移到 pyproject.toml</li>
</ol>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://packaging.python.org/">Python Packaging User Guide</a></li>
<li><a href="https://hatch.pypa.io/">Hatch 官方文件</a></li>
<li><a href="https://python-poetry.org/docs/">Poetry 官方文件</a></li>
<li><a href="https://flit.pypa.io/">Flit 官方文件</a></li>
</ul>
<hr>
<p><em>上一章：<a href="/blog/python-advanced/07-packaging/pyproject-toml/" data-link-title="6.1 pyproject.toml 完整指南" data-link-desc="理解現代 Python 套件的設定標準">pyproject.toml 完整指南</a></em>
<em>下一章：<a href="/blog/python-advanced/07-packaging/distribution/" data-link-title="6.3 發布到 PyPI" data-link-desc="學習如何建構 wheel 並發布到 PyPI">發布到 PyPI</a></em></p>
]]></content:encoded></item><item><title>案例：使用 Hatch 完整工作流</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/hatch-workflow/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/hatch-workflow/</guid><description>&lt;p>本案例展示如何使用 Hatch 這個 PyPA 推薦的現代 Python 專案管理工具，完成從專案建立到發布的完整流程。&lt;/p>
&lt;h2 id="先備知識">先備知識&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">6.2 建構系統比較&lt;/a>&lt;/li>
&lt;li>Python 虛擬環境基礎&lt;/li>
&lt;li>基本的命令列操作&lt;/li>
&lt;/ul>
&lt;h2 id="問題背景">問題背景&lt;/h2>
&lt;h3 id="hatch-是什麼">Hatch 是什麼？&lt;/h3>
&lt;p>Hatch 是由 PyPA（Python Packaging Authority）成員開發維護的現代 Python 專案管理工具，整合了：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Hatch 功能整合：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── 專案腳手架（hatch new）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── 環境管理（類似 tox + virtualenv）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 版本管理（自動更新版本號）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├── 建構系統（hatchling）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">└── 發布工具（hatch publish）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="為什麼選擇-hatch">為什麼選擇 Hatch？&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>優勢&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>標準優先&lt;/strong>&lt;/td>
 &lt;td>完全遵循 PEP 517/518/621 標準&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>一站式工具&lt;/strong>&lt;/td>
 &lt;td>不需要額外安裝 tox、virtualenv、bump2version&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>快速建構&lt;/strong>&lt;/td>
 &lt;td>hatchling 建構速度優於 setuptools&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>環境矩陣&lt;/strong>&lt;/td>
 &lt;td>內建多 Python 版本測試支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>腳本系統&lt;/strong>&lt;/td>
 &lt;td>定義可重用的專案腳本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="完整工作流">完整工作流&lt;/h2>
&lt;h3 id="第一步安裝-hatch">第一步：安裝 Hatch&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 pip 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">pip install hatch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或使用 pipx（推薦，隔離安裝）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">pipx install hatch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 驗證安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">hatch --version&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第二步建立新專案">第二步：建立新專案&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建立新專案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">hatch new my-awesome-lib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 互動式建立（可自訂選項）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">hatch new my-awesome-lib --init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建立應用程式專案（非函式庫）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">hatch new --cli my-cli-app&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="預設專案結構">預設專案結構&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">my-awesome-lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">│ └── my_awesome_lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">│ └── __about__.py # 版本資訊
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ └── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">└── LICENSE.txt&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="生成的-pyprojecttoml">生成的 pyproject.toml&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;hatchling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;hatchling.build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-awesome-lib&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">dynamic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;MIT&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">keywords&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">authors&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;you@example.com&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">classifiers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Development Status :: 4 - Beta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.8&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.11&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: 3.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">urls&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="nx">Documentation&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/my-awesome-lib#readme&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="nx">Issues&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/my-awesome-lib/issues&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="nx">Source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/yourname/my-awesome-lib&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/my_awesome_lib/__about__.py&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第三步環境管理hatch-env">第三步：環境管理（hatch env）&lt;/h3>
&lt;h4 id="定義環境">定義環境&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c"># 預設環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest-cov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">test&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">test-cov&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest --cov=my_awesome_lib --cov-report=term-missing {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c"># Lint 環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff&amp;gt;=0.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mypy&amp;gt;=1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="nx">check&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff check src tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff format --check src tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="nx">fix&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff check --fix src tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff format src tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="nx">typing&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;mypy src&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="nx">all&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;check&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;typing&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="c"># 文件環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mkdocs&amp;gt;=1.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mkdocs-material&amp;gt;=9.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">docs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">&lt;span class="nx">build&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;mkdocs build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl">&lt;span class="nx">serve&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;mkdocs serve&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="使用環境">使用環境&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 顯示所有環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">hatch env show
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 執行預設環境的腳本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">hatch run &lt;span class="nb">test&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">hatch run test-cov
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># 執行特定環境的腳本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">hatch run lint:check
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">hatch run lint:fix
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">hatch run lint:typing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># 進入環境 shell&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">hatch shell
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="c1"># 進入特定環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">hatch shell lint
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="c1"># 移除環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">hatch env remove
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">hatch env remove lint
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="c1"># 清除所有環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">hatch env prune&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第四步版本管理hatch-version">第四步：版本管理（hatch version）&lt;/h3>
&lt;h4 id="設定版本來源">設定版本來源&lt;/h4>
&lt;h5 id="方法-a從檔案讀取版本">方法 A：從檔案讀取版本&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/my_awesome_lib/__about__.py&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># src/my_awesome_lib/__about__.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="n">__version__&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="方法-b從-git-標籤讀取版本推薦用於開源專案">方法 B：從 Git 標籤讀取版本（推薦用於開源專案）&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;hatchling&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;hatch-vcs&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;hatchling.build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nx">source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;vcs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hooks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">vcs&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="nx">version-file&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/my_awesome_lib/_version.py&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="版本操作">版本操作&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 顯示當前版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">hatch version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 設定特定版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">hatch version 1.0.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 語意化版本升級&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">hatch version patch &lt;span class="c1"># 0.1.0 → 0.1.1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">hatch version minor &lt;span class="c1"># 0.1.1 → 0.2.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">hatch version major &lt;span class="c1"># 0.2.0 → 1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># 預發布版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">hatch version alpha &lt;span class="c1"># 1.0.0 → 1.0.1a0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">hatch version beta &lt;span class="c1"># 1.0.1a0 → 1.0.1b0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">hatch version rc &lt;span class="c1"># 1.0.1b0 → 1.0.1rc0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">hatch version release &lt;span class="c1"># 1.0.1rc0 → 1.0.1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c1"># 開發版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">hatch version dev &lt;span class="c1"># 1.0.0 → 1.0.1.dev0&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第五步建構與發布">第五步：建構與發布&lt;/h3>
&lt;h4 id="建構套件">建構套件&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構 wheel 和 sdist&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">hatch build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只建構 wheel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">hatch build --target wheel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只建構 sdist&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">hatch build --target sdist
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 清除建構產物後重建&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">hatch build --clean&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="建構產物">建構產物&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">dist/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── my_awesome_lib-0.1.0-py3-none-any.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">└── my_awesome_lib-0.1.0.tar.gz&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="發布套件">發布套件&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布到 PyPI（需要設定認證）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">hatch publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布到 TestPyPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">hatch publish --repo &lt;span class="nb">test&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 指定發布的檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">hatch publish dist/my_awesome_lib-0.1.0-py3-none-any.whl&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="設定-pypi-認證">設定 PyPI 認證&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 設定 PyPI token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">hatch config &lt;span class="nb">set&lt;/span> pypi.auth.username __token__
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">hatch config &lt;span class="nb">set&lt;/span> pypi.auth.password pypi-xxxxx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或使用環境變數&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">HATCH_INDEX_USER&lt;/span>&lt;span class="o">=&lt;/span>__token__
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">HATCH_INDEX_AUTH&lt;/span>&lt;span class="o">=&lt;/span>pypi-xxxxx&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="pyprojecttoml-的-hatch-特定設定">pyproject.toml 的 Hatch 特定設定&lt;/h2>
&lt;h3 id="toolhatchbuild-建構設定">[tool.hatch.build] 建構設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c"># 包含的檔案（支援 glob）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">include&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;src/my_awesome_lib&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c"># 排除的檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">exclude&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;*.pyc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;__pycache__&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;.git&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c"># 是否可重現建構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">reproducible&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c"># 開發模式設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">dev-mode-dirs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">targets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sdist&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="c"># 原始碼發布設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="nx">include&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;/src&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;/tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;/README.md&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;/LICENSE.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">targets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">wheel&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="c"># Wheel 發布設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src/my_awesome_lib&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="c"># 只包含特定平台&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="c"># only-include = [&amp;#34;my_awesome_lib&amp;#34;]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="toolhatchenvs-環境設定">[tool.hatch.envs] 環境設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c"># 相依性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c"># 額外安裝的 features&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">features&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;yaml&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c"># 環境變數&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env-vars&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">PYTHONPATH&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">LOG_LEVEL&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;DEBUG&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c"># 腳本定義&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">test&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest {args}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="c"># 平台特定設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">overrides&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">platform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">windows&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="s1">&amp;#39;test = &amp;#34;pytest --no-header {args}&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="toolhatchversion-版本設定">[tool.hatch.version] 版本設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># 從檔案讀取&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/my_awesome_lib/__about__.py&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">pattern&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^__version__ = [&amp;#39;\&amp;#34;](/python-advanced/07-packaging/case-studies/hatch-workflow/?P&amp;lt;version&amp;gt;[^&amp;#39;\&amp;#34;]+)[&amp;#39;\&amp;#34;]&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c"># 從 VCS 讀取&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;vcs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">raw-options&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">local_scheme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;no-local-version&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hooks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">vcs&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">version-file&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/my_awesome_lib/_version.py&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="toolhatchmetadata-元資料設定">[tool.hatch.metadata] 元資料設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">metadata&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c"># 允許直接依賴（通常應該避免）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">allow-direct-references&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c"># 動態讀取 README&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">metadata&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hooks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fancy-pypi-readme&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">content-type&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;text/markdown&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="p">[[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">metadata&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hooks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fancy-pypi-readme&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fragments&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="與-poetry-的比較">與 Poetry 的比較&lt;/h2>
&lt;h3 id="設計理念差異">設計理念差異&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">Hatch：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 遵循 PEP 標準優先
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 環境管理內建
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 不提供依賴鎖定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── 建構系統（hatchling）可獨立使用
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">└── 設定完全在 [tool.hatch]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">Poetry：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 自有生態系統
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 強調依賴鎖定（poetry.lock）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">├── 虛擬環境管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── poetry.core 可獨立使用
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">└── 混合 [project] 和 [tool.poetry]&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="功能對照">功能對照&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>功能&lt;/th>
 &lt;th>Hatch&lt;/th>
 &lt;th>Poetry&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>依賴鎖定&lt;/td>
 &lt;td>不支援&lt;/td>
 &lt;td>poetry.lock&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>環境管理&lt;/td>
 &lt;td>內建矩陣支援&lt;/td>
 &lt;td>單一環境&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PEP 621&lt;/td>
 &lt;td>完全支援&lt;/td>
 &lt;td>Poetry 2.0 支援&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>腳本系統&lt;/td>
 &lt;td>強大（環境分離）&lt;/td>
 &lt;td>基本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>版本管理&lt;/td>
 &lt;td>內建 bump&lt;/td>
 &lt;td>需外掛或手動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>插件系統&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;td>支援&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="pyprojecttoml-比較">pyproject.toml 比較&lt;/h3>
&lt;h4 id="hatch-風格">Hatch 風格&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;hatchling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;hatchling.build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requests&amp;gt;=2.28&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="nx">features&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;dev&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="poetry-風格20">Poetry 風格（2.0）&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;poetry-core&amp;gt;=2.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;poetry.core.masonry.api&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;requests&amp;gt;=2.28&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">poetry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">pytest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;^8.0&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="選擇建議">選擇建議&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">選擇 Hatch：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 開發 Python 函式庫
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 需要多環境測試
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 偏好標準優先
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">└── 不需要依賴鎖定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">選擇 Poetry：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── 開發應用程式
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 需要嚴格的依賴鎖定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 團隊習慣 Poetry 工作流
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">└── 需要與現有 Poetry 專案整合&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="實用技巧">實用技巧&lt;/h2>
&lt;h3 id="多環境測試矩陣">多環境測試矩陣&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># 定義多 Python 版本測試&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pytest-cov&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matrix&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;3.9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3.11&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3.13&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">run&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">cov&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest --cov=my_awesome_lib {args:tests}&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在所有矩陣環境執行測試&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">hatch run test:run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在特定版本執行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">hatch run +py&lt;span class="o">=&lt;/span>3.12 test:run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 顯示矩陣環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">hatch env show --ascii&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="複合腳本">複合腳本&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c"># 單一命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">test&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c"># 多命令（依序執行）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">ci&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff check src tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest --cov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mypy src&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c"># 呼叫其他腳本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">all&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;lint:check&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;test:cov&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c"># 帶預設參數&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">lint&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;ruff check {args:.}&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="環境繼承">環境繼承&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># 基礎環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">base&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c"># 繼承基礎環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">coverage&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">template&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;base&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest-cov&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">coverage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">run&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest --cov {args:tests}&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="條件依賴">條件依賴&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c"># 根據平台新增依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">overrides&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">platform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">linux&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest-xdist&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">platform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">darwin&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest-xdist&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c"># 根據 Python 版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">python&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mf">3.8&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;typing-extensions&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="自訂建構-hook">自訂建構 Hook&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hooks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">custom&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c"># 建構前執行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;hatch_build.py&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># hatch_build.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">hatchling.builders.hooks.plugin.interface&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BuildHookInterface&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CustomBuildHook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BuildHookInterface&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">initialize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">version&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">build_data&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 建構前的自訂邏輯&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Building version &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">version&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="完整範例cli-應用程式">完整範例：CLI 應用程式&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;hatchling&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;hatchling.build&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-cli&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">dynamic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;A useful CLI tool&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">readme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;README.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;MIT&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;click&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;rich&amp;gt;=13.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">my-cli&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_cli.main:app&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="nx">yaml&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;PyYAML&amp;gt;=6.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/my_cli/__about__.py&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">build&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">targets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">wheel&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="nx">packages&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src/my_cli&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="c"># ===== 環境設定 =====&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest-cov&amp;gt;=4.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">default&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">&lt;span class="nx">test&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="nx">cov&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;pytest --cov=my_cli --cov-report=term-missing {args:tests}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">&lt;span class="nx">dependencies&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff&amp;gt;=0.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mypy&amp;gt;=1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scripts&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl">&lt;span class="nx">check&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;ruff check src tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;ruff format --check src tests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl">&lt;span class="nx">fix&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;ruff check --fix src tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;ruff format src tests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl">&lt;span class="nx">typing&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;mypy src&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl">&lt;span class="nx">all&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;check&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;typing&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl">&lt;span class="p">[[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hatch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">envs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matrix&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl">&lt;span class="nx">python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3.11&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3.13&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">&lt;span class="c"># ===== 工具設定 =====&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl">&lt;span class="nx">src&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl">&lt;span class="nx">line-length&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">88&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl">&lt;span class="nx">select&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;W&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;F&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;I&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;B&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;UP&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">65&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">66&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mypy&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">67&lt;/span>&lt;span class="cl">&lt;span class="nx">python_version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">68&lt;/span>&lt;span class="cl">&lt;span class="nx">strict&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">69&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">70&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pytest&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ini_options&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">71&lt;/span>&lt;span class="cl">&lt;span class="nx">testpaths&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">72&lt;/span>&lt;span class="cl">&lt;span class="nx">addopts&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;-v&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="發布檢查清單">發布檢查清單&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">發布前檢查：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── [ ] hatch run lint:all 通過
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── [ ] hatch run test:run 在所有 Python 版本通過
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── [ ] hatch version 更新版本號
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── [ ] 更新 CHANGELOG.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── [ ] hatch build 建構成功
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── [ ] 在虛擬環境測試安裝：pip install dist/*.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── [ ] hatch publish --repo test 發布到 TestPyPI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── [ ] 從 TestPyPI 測試安裝
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">└── [ ] hatch publish 發布到 PyPI&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="延伸閱讀">延伸閱讀&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://hatch.pypa.io/">Hatch 官方文件&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://hatch.pypa.io/latest/plugins/build-hook/reference/">Hatchling 建構後端&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://peps.python.org/pep-0621/">PEP 621 - pyproject.toml 元資料&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://packaging.python.org/">Python 打包使用者指南&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>返回：&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/" data-link-title="案例研究" data-link-desc="基於 .claude/lib 實際程式碼的打包發布案例">案例研究&lt;/a>&lt;/p></description><content:encoded><![CDATA[<p>本案例展示如何使用 Hatch 這個 PyPA 推薦的現代 Python 專案管理工具，完成從專案建立到發布的完整流程。</p>
<h2 id="先備知識">先備知識</h2>
<ul>
<li><a href="/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">6.2 建構系統比較</a></li>
<li>Python 虛擬環境基礎</li>
<li>基本的命令列操作</li>
</ul>
<h2 id="問題背景">問題背景</h2>
<h3 id="hatch-是什麼">Hatch 是什麼？</h3>
<p>Hatch 是由 PyPA（Python Packaging Authority）成員開發維護的現代 Python 專案管理工具，整合了：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Hatch 功能整合：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 專案腳手架（hatch new）
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 環境管理（類似 tox + virtualenv）
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 版本管理（自動更新版本號）
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── 建構系統（hatchling）
</span></span><span class="line"><span class="ln">6</span><span class="cl">└── 發布工具（hatch publish）</span></span></code></pre></div><h3 id="為什麼選擇-hatch">為什麼選擇 Hatch？</h3>
<table>
  <thead>
      <tr>
          <th>優勢</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>標準優先</strong></td>
          <td>完全遵循 PEP 517/518/621 標準</td>
      </tr>
      <tr>
          <td><strong>一站式工具</strong></td>
          <td>不需要額外安裝 tox、virtualenv、bump2version</td>
      </tr>
      <tr>
          <td><strong>快速建構</strong></td>
          <td>hatchling 建構速度優於 setuptools</td>
      </tr>
      <tr>
          <td><strong>環境矩陣</strong></td>
          <td>內建多 Python 版本測試支援</td>
      </tr>
      <tr>
          <td><strong>腳本系統</strong></td>
          <td>定義可重用的專案腳本</td>
      </tr>
  </tbody>
</table>
<h2 id="完整工作流">完整工作流</h2>
<h3 id="第一步安裝-hatch">第一步：安裝 Hatch</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 使用 pip 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">pip install hatch
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 或使用 pipx（推薦，隔離安裝）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">pipx install hatch
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 驗證安裝</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">hatch --version</span></span></code></pre></div><h3 id="第二步建立新專案">第二步：建立新專案</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 建立新專案</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hatch new my-awesome-lib
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 互動式建立（可自訂選項）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">hatch new my-awesome-lib --init
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 建立應用程式專案（非函式庫）</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">hatch new --cli my-cli-app</span></span></code></pre></div><h4 id="預設專案結構">預設專案結構</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">my-awesome-lib/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">│   └── my_awesome_lib/
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">│       ├── __init__.py
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│       └── __about__.py      # 版本資訊
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── tests/
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│   └── __init__.py
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln">10</span><span class="cl">└── LICENSE.txt</span></span></code></pre></div><h4 id="生成的-pyprojecttoml">生成的 pyproject.toml</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-awesome-lib&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.8&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">keywords</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">authors</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="p">{</span> <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">,</span> <span class="nx">email</span> <span class="p">=</span> <span class="s2">&#34;you@example.com&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">classifiers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="s2">&#34;Development Status :: 4 - Beta&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="s2">&#34;Programming Language :: Python&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="s2">&#34;Programming Language :: Python :: 3.8&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="s2">&#34;Programming Language :: Python :: 3.9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="s2">&#34;Programming Language :: Python :: 3.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="s2">&#34;Programming Language :: Python :: 3.11&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="s2">&#34;Programming Language :: Python :: 3.12&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">urls</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="nx">Documentation</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/my-awesome-lib#readme&#34;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="nx">Issues</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/my-awesome-lib/issues&#34;</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">Source</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/yourname/my-awesome-lib&#34;</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;src/my_awesome_lib/__about__.py&#34;</span></span></span></code></pre></div><h3 id="第三步環境管理hatch-env">第三步：環境管理（hatch env）</h3>
<h4 id="定義環境">定義環境</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c"># 預設環境</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="s2">&#34;pytest&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="s2">&#34;pytest-cov&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">test</span> <span class="p">=</span> <span class="s2">&#34;pytest {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">test-cov</span> <span class="p">=</span> <span class="s2">&#34;pytest --cov=my_awesome_lib --cov-report=term-missing {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c"># Lint 環境</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="s2">&#34;ruff&gt;=0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="s2">&#34;mypy&gt;=1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">lint</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">check</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="s2">&#34;ruff check src tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="s2">&#34;ruff format --check src tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="nx">fix</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="s2">&#34;ruff check --fix src tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="s2">&#34;ruff format src tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">typing</span> <span class="p">=</span> <span class="s2">&#34;mypy src&#34;</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;check&#34;</span><span class="p">,</span> <span class="s2">&#34;typing&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="c"># 文件環境</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">docs</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="s2">&#34;mkdocs&gt;=1.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="s2">&#34;mkdocs-material&gt;=9.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">docs</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="nx">build</span> <span class="p">=</span> <span class="s2">&#34;mkdocs build&#34;</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="nx">serve</span> <span class="p">=</span> <span class="s2">&#34;mkdocs serve&#34;</span></span></span></code></pre></div><h4 id="使用環境">使用環境</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 顯示所有環境</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">hatch env show
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 執行預設環境的腳本</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">hatch run <span class="nb">test</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">hatch run test-cov
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># 執行特定環境的腳本</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">hatch run lint:check
</span></span><span class="line"><span class="ln">10</span><span class="cl">hatch run lint:fix
</span></span><span class="line"><span class="ln">11</span><span class="cl">hatch run lint:typing
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 進入環境 shell</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">hatch shell
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># 進入特定環境</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">hatch shell lint
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"># 移除環境</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">hatch env remove
</span></span><span class="line"><span class="ln">21</span><span class="cl">hatch env remove lint
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"># 清除所有環境</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">hatch env prune</span></span></code></pre></div><h3 id="第四步版本管理hatch-version">第四步：版本管理（hatch version）</h3>
<h4 id="設定版本來源">設定版本來源</h4>
<h5 id="方法-a從檔案讀取版本">方法 A：從檔案讀取版本</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;src/my_awesome_lib/__about__.py&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># src/my_awesome_lib/__about__.py</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">__version__</span> <span class="o">=</span> <span class="s2">&#34;0.1.0&#34;</span></span></span></code></pre></div><h5 id="方法-b從-git-標籤讀取版本推薦用於開源專案">方法 B：從 Git 標籤讀取版本（推薦用於開源專案）</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">,</span> <span class="s2">&#34;hatch-vcs&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">source</span> <span class="p">=</span> <span class="s2">&#34;vcs&#34;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">vcs</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="nx">version-file</span> <span class="p">=</span> <span class="s2">&#34;src/my_awesome_lib/_version.py&#34;</span></span></span></code></pre></div><h4 id="版本操作">版本操作</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 顯示當前版本</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">hatch version
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 設定特定版本</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">hatch version 1.0.0
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 語意化版本升級</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">hatch version patch   <span class="c1"># 0.1.0 → 0.1.1</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">hatch version minor   <span class="c1"># 0.1.1 → 0.2.0</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">hatch version major   <span class="c1"># 0.2.0 → 1.0.0</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 預發布版本</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">hatch version alpha   <span class="c1"># 1.0.0 → 1.0.1a0</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">hatch version beta    <span class="c1"># 1.0.1a0 → 1.0.1b0</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">hatch version rc      <span class="c1"># 1.0.1b0 → 1.0.1rc0</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">hatch version release <span class="c1"># 1.0.1rc0 → 1.0.1</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># 開發版本</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">hatch version dev     <span class="c1"># 1.0.0 → 1.0.1.dev0</span></span></span></code></pre></div><h3 id="第五步建構與發布">第五步：建構與發布</h3>
<h4 id="建構套件">建構套件</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 建構 wheel 和 sdist</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">hatch build
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 只建構 wheel</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">hatch build --target wheel
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 只建構 sdist</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">hatch build --target sdist
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 清除建構產物後重建</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">hatch build --clean</span></span></code></pre></div><h5 id="建構產物">建構產物</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">dist/
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── my_awesome_lib-0.1.0-py3-none-any.whl
</span></span><span class="line"><span class="ln">3</span><span class="cl">└── my_awesome_lib-0.1.0.tar.gz</span></span></code></pre></div><h4 id="發布套件">發布套件</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 發布到 PyPI（需要設定認證）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hatch publish
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 發布到 TestPyPI</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">hatch publish --repo <span class="nb">test</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 指定發布的檔案</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">hatch publish dist/my_awesome_lib-0.1.0-py3-none-any.whl</span></span></code></pre></div><h5 id="設定-pypi-認證">設定 PyPI 認證</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 設定 PyPI token</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hatch config <span class="nb">set</span> pypi.auth.username __token__
</span></span><span class="line"><span class="ln">3</span><span class="cl">hatch config <span class="nb">set</span> pypi.auth.password pypi-xxxxx
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 或使用環境變數</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nb">export</span> <span class="nv">HATCH_INDEX_USER</span><span class="o">=</span>__token__
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nb">export</span> <span class="nv">HATCH_INDEX_AUTH</span><span class="o">=</span>pypi-xxxxx</span></span></code></pre></div><h2 id="pyprojecttoml-的-hatch-特定設定">pyproject.toml 的 Hatch 特定設定</h2>
<h3 id="toolhatchbuild-建構設定">[tool.hatch.build] 建構設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># 包含的檔案（支援 glob）</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">include</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="s2">&#34;src/my_awesome_lib&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="s2">&#34;README.md&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c"># 排除的檔案</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">exclude</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="s2">&#34;*.pyc&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="s2">&#34;__pycache__&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="s2">&#34;.git&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c"># 是否可重現建構</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">reproducible</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c"># 開發模式設定</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">dev-mode-dirs</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">sdist</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c"># 原始碼發布設定</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nx">include</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="s2">&#34;/src&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="s2">&#34;/tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="s2">&#34;/README.md&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="s2">&#34;/LICENSE.txt&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="c"># Wheel 發布設定</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/my_awesome_lib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c"># 只包含特定平台</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c"># only-include = [&#34;my_awesome_lib&#34;]</span></span></span></code></pre></div><h3 id="toolhatchenvs-環境設定">[tool.hatch.envs] 環境設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># 相依性</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c"># 額外安裝的 features</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;yaml&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c"># 環境變數</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">env-vars</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">PYTHONPATH</span> <span class="p">=</span> <span class="s2">&#34;src&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">LOG_LEVEL</span> <span class="p">=</span> <span class="s2">&#34;DEBUG&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c"># 腳本定義</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">test</span> <span class="p">=</span> <span class="s2">&#34;pytest {args}&#34;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c"># 平台特定設定</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">overrides</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">platform</span><span class="p">.</span><span class="nx">windows</span><span class="p">.</span><span class="nx">scripts</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="s1">&#39;test = &#34;pytest --no-header {args}&#34;&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">]</span></span></span></code></pre></div><h3 id="toolhatchversion-版本設定">[tool.hatch.version] 版本設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 從檔案讀取</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;src/my_awesome_lib/__about__.py&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">pattern</span> <span class="p">=</span> <span class="s2">&#34;^__version__ = [&#39;\&#34;](/python-advanced/07-packaging/case-studies/hatch-workflow/?P&lt;version&gt;[^&#39;\&#34;]+)[&#39;\&#34;]&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c"># 從 VCS 讀取</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">source</span> <span class="p">=</span> <span class="s2">&#34;vcs&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">raw-options</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">local_scheme</span> <span class="p">=</span> <span class="s2">&#34;no-local-version&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">vcs</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">version-file</span> <span class="p">=</span> <span class="s2">&#34;src/my_awesome_lib/_version.py&#34;</span></span></span></code></pre></div><h3 id="toolhatchmetadata-元資料設定">[tool.hatch.metadata] 元資料設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">metadata</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># 允許直接依賴（通常應該避免）</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">allow-direct-references</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c"># 動態讀取 README</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">metadata</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">fancy-pypi-readme</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">content-type</span> <span class="p">=</span> <span class="s2">&#34;text/markdown&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">[[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">metadata</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">fancy-pypi-readme</span><span class="p">.</span><span class="nx">fragments</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span></span></span></code></pre></div><h2 id="與-poetry-的比較">與 Poetry 的比較</h2>
<h3 id="設計理念差異">設計理念差異</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">Hatch：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 遵循 PEP 標準優先
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 環境管理內建
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 不提供依賴鎖定
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── 建構系統（hatchling）可獨立使用
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">└── 設定完全在 [tool.hatch]
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">Poetry：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 自有生態系統
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 強調依賴鎖定（poetry.lock）
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── 虛擬環境管理
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── poetry.core 可獨立使用
</span></span><span class="line"><span class="ln">13</span><span class="cl">└── 混合 [project] 和 [tool.poetry]</span></span></code></pre></div><h3 id="功能對照">功能對照</h3>
<table>
  <thead>
      <tr>
          <th>功能</th>
          <th>Hatch</th>
          <th>Poetry</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>依賴鎖定</td>
          <td>不支援</td>
          <td>poetry.lock</td>
      </tr>
      <tr>
          <td>環境管理</td>
          <td>內建矩陣支援</td>
          <td>單一環境</td>
      </tr>
      <tr>
          <td>PEP 621</td>
          <td>完全支援</td>
          <td>Poetry 2.0 支援</td>
      </tr>
      <tr>
          <td>腳本系統</td>
          <td>強大（環境分離）</td>
          <td>基本</td>
      </tr>
      <tr>
          <td>版本管理</td>
          <td>內建 bump</td>
          <td>需外掛或手動</td>
      </tr>
      <tr>
          <td>插件系統</td>
          <td>支援</td>
          <td>支援</td>
      </tr>
  </tbody>
</table>
<h3 id="pyprojecttoml-比較">pyproject.toml 比較</h3>
<h4 id="hatch-風格">Hatch 風格</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&gt;=2.28&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&gt;=8.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;dev&#34;</span><span class="p">]</span></span></span></code></pre></div><h4 id="poetry-風格20">Poetry 風格（2.0）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;poetry-core&gt;=2.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;poetry.core.masonry.api&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.0.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;requests&gt;=2.28&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">poetry</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">dev</span><span class="p">.</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">pytest</span> <span class="p">=</span> <span class="s2">&#34;^8.0&#34;</span></span></span></code></pre></div><h3 id="選擇建議">選擇建議</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">選擇 Hatch：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 開發 Python 函式庫
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 需要多環境測試
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 偏好標準優先
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── 不需要依賴鎖定
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">選擇 Poetry：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 開發應用程式
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 需要嚴格的依賴鎖定
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 團隊習慣 Poetry 工作流
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── 需要與現有 Poetry 專案整合</span></span></code></pre></div><h2 id="實用技巧">實用技巧</h2>
<h3 id="多環境測試矩陣">多環境測試矩陣</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 定義多 Python 版本測試</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">test</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&#34;</span><span class="p">,</span> <span class="s2">&#34;pytest-cov&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">matrix</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">python</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;3.9&#34;</span><span class="p">,</span> <span class="s2">&#34;3.10&#34;</span><span class="p">,</span> <span class="s2">&#34;3.11&#34;</span><span class="p">,</span> <span class="s2">&#34;3.12&#34;</span><span class="p">,</span> <span class="s2">&#34;3.13&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;pytest {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">cov</span> <span class="p">=</span> <span class="s2">&#34;pytest --cov=my_awesome_lib {args:tests}&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 在所有矩陣環境執行測試</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hatch run test:run
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 在特定版本執行</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">hatch run +py<span class="o">=</span>3.12 test:run
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 顯示矩陣環境</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">hatch env show --ascii</span></span></code></pre></div><h3 id="複合腳本">複合腳本</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c"># 單一命令</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">test</span> <span class="p">=</span> <span class="s2">&#34;pytest {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c"># 多命令（依序執行）</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">ci</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="s2">&#34;ruff check src tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="s2">&#34;pytest --cov&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="s2">&#34;mypy src&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c"># 呼叫其他腳本</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;lint:check&#34;</span><span class="p">,</span> <span class="s2">&#34;test:cov&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c"># 帶預設參數</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">lint</span> <span class="p">=</span> <span class="s2">&#34;ruff check {args:.}&#34;</span></span></span></code></pre></div><h3 id="環境繼承">環境繼承</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 基礎環境</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">base</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c"># 繼承基礎環境</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">coverage</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">template</span> <span class="p">=</span> <span class="s2">&#34;base&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest-cov&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">coverage</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;pytest --cov {args:tests}&#34;</span></span></span></code></pre></div><h3 id="條件依賴">條件依賴</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="s2">&#34;pytest&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c"># 根據平台新增依賴</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">overrides</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">platform</span><span class="p">.</span><span class="nx">linux</span><span class="p">.</span><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest-xdist&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">platform</span><span class="p">.</span><span class="nx">darwin</span><span class="p">.</span><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest-xdist&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c"># 根據 Python 版本</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">python</span><span class="p">.</span><span class="mf">3.8</span><span class="p">.</span><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;typing-extensions&#34;</span><span class="p">]</span></span></span></code></pre></div><h3 id="自訂建構-hook">自訂建構 Hook</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">custom</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c"># 建構前執行</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;hatch_build.py&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># hatch_build.py</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">from</span> <span class="nn">hatchling.builders.hooks.plugin.interface</span> <span class="kn">import</span> <span class="n">BuildHookInterface</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">class</span> <span class="nc">CustomBuildHook</span><span class="p">(</span><span class="n">BuildHookInterface</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">version</span><span class="p">,</span> <span class="n">build_data</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">        <span class="c1"># 建構前的自訂邏輯</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Building version </span><span class="si">{</span><span class="n">version</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span></span></span></code></pre></div><h2 id="完整範例cli-應用程式">完整範例：CLI 應用程式</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-cli&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;A useful CLI tool&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">readme</span> <span class="p">=</span> <span class="s2">&#34;README.md&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.10&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">license</span> <span class="p">=</span> <span class="s2">&#34;MIT&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="s2">&#34;click&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="s2">&#34;rich&gt;=13.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">my-cli</span> <span class="p">=</span> <span class="s2">&#34;my_cli.main:app&#34;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">yaml</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;PyYAML&gt;=6.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">version</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;src/my_cli/__about__.py&#34;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="nx">packages</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/my_cli&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c"># ===== 環境設定 =====</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="s2">&#34;pytest&gt;=8.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="s2">&#34;pytest-cov&gt;=4.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="nx">test</span> <span class="p">=</span> <span class="s2">&#34;pytest {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="nx">cov</span> <span class="p">=</span> <span class="s2">&#34;pytest --cov=my_cli --cov-report=term-missing {args:tests}&#34;</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="nx">dependencies</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">  <span class="s2">&#34;ruff&gt;=0.4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">  <span class="s2">&#34;mypy&gt;=1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">lint</span><span class="p">.</span><span class="nx">scripts</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="nx">check</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;ruff check src tests&#34;</span><span class="p">,</span> <span class="s2">&#34;ruff format --check src tests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="nx">fix</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;ruff check --fix src tests&#34;</span><span class="p">,</span> <span class="s2">&#34;ruff format src tests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="nx">typing</span> <span class="p">=</span> <span class="s2">&#34;mypy src&#34;</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;check&#34;</span><span class="p">,</span> <span class="s2">&#34;typing&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="p">[[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">envs</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">matrix</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="nx">python</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;3.10&#34;</span><span class="p">,</span> <span class="s2">&#34;3.11&#34;</span><span class="p">,</span> <span class="s2">&#34;3.12&#34;</span><span class="p">,</span> <span class="s2">&#34;3.13&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="c"># ===== 工具設定 =====</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">
</span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="nx">src</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="nx">line-length</span> <span class="p">=</span> <span class="mi">88</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="nx">select</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;E&#34;</span><span class="p">,</span> <span class="s2">&#34;W&#34;</span><span class="p">,</span> <span class="s2">&#34;F&#34;</span><span class="p">,</span> <span class="s2">&#34;I&#34;</span><span class="p">,</span> <span class="s2">&#34;B&#34;</span><span class="p">,</span> <span class="s2">&#34;UP&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">
</span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">mypy</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="nx">python_version</span> <span class="p">=</span> <span class="s2">&#34;3.10&#34;</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="nx">strict</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl">
</span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">pytest</span><span class="p">.</span><span class="nx">ini_options</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="nx">testpaths</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;tests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="nx">addopts</span> <span class="p">=</span> <span class="s2">&#34;-v&#34;</span></span></span></code></pre></div><h2 id="發布檢查清單">發布檢查清單</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">發布前檢查：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── [ ] hatch run lint:all 通過
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── [ ] hatch run test:run 在所有 Python 版本通過
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── [ ] hatch version 更新版本號
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── [ ] 更新 CHANGELOG.md
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── [ ] hatch build 建構成功
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── [ ] 在虛擬環境測試安裝：pip install dist/*.whl
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── [ ] hatch publish --repo test 發布到 TestPyPI
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── [ ] 從 TestPyPI 測試安裝
</span></span><span class="line"><span class="ln">10</span><span class="cl">└── [ ] hatch publish 發布到 PyPI</span></span></code></pre></div><h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://hatch.pypa.io/">Hatch 官方文件</a></li>
<li><a href="https://hatch.pypa.io/latest/plugins/build-hook/reference/">Hatchling 建構後端</a></li>
<li><a href="https://peps.python.org/pep-0621/">PEP 621 - pyproject.toml 元資料</a></li>
<li><a href="https://packaging.python.org/">Python 打包使用者指南</a></li>
</ul>
<hr>
<p>返回：<a href="/blog/python-advanced/07-packaging/case-studies/" data-link-title="案例研究" data-link-desc="基於 .claude/lib 實際程式碼的打包發布案例">案例研究</a></p>
]]></content:encoded></item><item><title>5.3 Maturin 開發流程</title><link>https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/maturin-workflow/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/maturin-workflow/</guid><description>&lt;p>本章介紹 Maturin，Rust Python 套件的建構工具。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>學完本章後，你將能夠：&lt;/p>
&lt;ol>
&lt;li>設定 Maturin 專案&lt;/li>
&lt;li>使用 maturin develop 快速迭代&lt;/li>
&lt;li>建構跨平台 wheel 並發布到 PyPI&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="原理層maturin-是什麼">【原理層】Maturin 是什麼？&lt;/h2>
&lt;h3 id="建構工具的角色">建構工具的角色&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">傳統 Python 擴展建構：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── setuptools + setup.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 複雜的編譯器設定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 手動處理連結問題
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">└── 跨平台建構困難
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">Maturin 提供：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── 一鍵建構 Rust Python 套件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 自動處理 PyO3 設定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 跨平台 wheel 生成
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">├── 與 pyproject.toml 整合
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">└── 開發模式快速迭代&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="支援的專案類型">支援的專案類型&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Maturin 支援：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── pyo3：純 Rust 擴展（最常用）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── cffi：生成 cffi 綁定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── uniffi：跨語言綁定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">└── bin：Rust 二進位程式打包&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="設計層專案設定">【設計層】專案設定&lt;/h2>
&lt;h3 id="安裝-maturin">安裝 Maturin&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 pip&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">pip install maturin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 pipx（推薦，隔離環境）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">pipx install maturin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 cargo&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">cargo install maturin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 驗證安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">maturin --version&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="建立新專案">建立新專案&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 互動式建立&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">maturin new my_rust_module
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my_rust_module
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或者指定綁定類型&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">maturin new my_rust_module --bindings pyo3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># 專案結構&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">my_rust_module/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── Cargo.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">│ └── lib.rs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">└── python/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> └── my_rust_module/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> └── __init__.py &lt;span class="c1"># 選用&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="pyprojecttoml-設定">pyproject.toml 設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;maturin&amp;gt;=1.5,&amp;lt;2.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;maturin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-rust-module&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;A Python module written in Rust&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">classifiers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Rust&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: Implementation :: CPython&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;Programming Language :: Python :: Implementation :: PyPy&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pytest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;hypothesis&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">maturin&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="c"># 模組名稱（如果與 crate 名稱不同）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="nx">module-name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_rust_module&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="c"># Python 原始碼目錄（如果有純 Python 程式碼）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="nx">python-source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="c"># 功能標誌&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="nx">features&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pyo3/extension-module&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="c"># 啟用 abi3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="c"># features = [&amp;#34;pyo3/extension-module&amp;#34;, &amp;#34;pyo3/abi3-py38&amp;#34;]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="c"># 排除不需要的檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="nx">exclude&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tests/*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;benches/*&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="c"># 建構設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="c"># strip = true # 移除除錯符號（減小檔案大小）&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="cargotoml-設定">Cargo.toml 設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">package&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_rust_module&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">edition&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;2021&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">lib&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_rust_module&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">crate-type&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;cdylib&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nx">pyo3&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.23&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">features&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;extension-module&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">profile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">release&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="nx">lto&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="c"># Link-Time Optimization&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">codegen-units&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="c"># 更好的優化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">strip&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="c"># 移除除錯符號&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">profile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dev&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">opt-level&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="c"># 快速編譯&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層開發流程">【實作層】開發流程&lt;/h2>
&lt;h3 id="maturin-develop">maturin develop&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 開發模式：編譯並安裝到當前虛擬環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">maturin develop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 release 模式（較慢但更快的執行時效能）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">maturin develop --release
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 指定功能&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">maturin develop --features &lt;span class="s2">&amp;#34;some-feature&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在其他虛擬環境中安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">maturin develop --target-dir target -E /path/to/venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用後立即測試&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">maturin develop &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> python -c &lt;span class="s2">&amp;#34;import my_rust_module; print(my_rust_module.add(1, 2))&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="完整開發循環">完整開發循環&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 建立虛擬環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">python -m venv .venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> .venv/bin/activate &lt;span class="c1"># Linux/macOS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># .venv\Scripts\activate # Windows&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 安裝開發依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">pip install maturin pytest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 開發循環&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="k">while&lt;/span> editing:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 編輯 Rust 程式碼&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> vim src/lib.rs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 建構並安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> maturin develop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 執行測試&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> pytest tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="c1"># 4. 準備發布&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">maturin build --release&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="混合-pythonrust-專案">混合 Python/Rust 專案&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">專案結構：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">my_package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── Cargo.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">│ └── lib.rs # Rust 程式碼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">└── python/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> └── my_package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> ├── __init__.py # 匯入 Rust 模組
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> ├── utils.py # 純 Python 程式碼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> └── py.typed # 型別標記&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># python/my_package/__init__.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">.my_package&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="c1"># 從 Rust 模組匯入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">.utils&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">helper_function&lt;/span> &lt;span class="c1"># 純 Python&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="n">__version__&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">maturin&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">python-source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="nx">module-name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my_package.my_package&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層建構與發布">【實作層】建構與發布&lt;/h2>
&lt;h3 id="maturin-build">maturin build&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構 wheel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">maturin build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># Release 模式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">maturin build --release
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 指定 Python 版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">maturin build --interpreter python3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 多個 Python 版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">maturin build --interpreter python3.10 python3.11 python3.12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 abi3（穩定 ABI）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">maturin build --release --features pyo3/abi3-py38
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構結果位置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">ls target/wheels/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c1"># my_rust_module-0.1.0-cp311-cp311-linux_x86_64.whl&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="跨平台建構">跨平台建構&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 Docker 建構 manylinux wheel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">maturin build --release --manylinux &lt;span class="m">2014&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 常用的 manylinux 版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># manylinux1: CentOS 5 (非常老舊)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># manylinux2010: CentOS 6&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># manylinux2014: CentOS 7 (推薦)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># manylinux_2_28: Debian 9 / Ubuntu 18.04&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 zig 進行交叉編譯&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">pip install ziglang
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">maturin build --release --target x86_64-unknown-linux-gnu --zig
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c1"># 常見目標平台&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c1"># x86_64-unknown-linux-gnu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="c1"># aarch64-unknown-linux-gnu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="c1"># x86_64-apple-darwin&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c1"># aarch64-apple-darwin&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="c1"># x86_64-pc-windows-msvc&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="發布到-pypi">發布到 PyPI&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布到 TestPyPI（測試）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">maturin publish --repository testpypi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布到 PyPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">maturin publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 API token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">maturin publish --username __token__ --password &amp;lt;your-pypi-token&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或者設定環境變數&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">MATURIN_PYPI_TOKEN&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;your-pypi-token&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">maturin publish&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層cicd-整合">【實作層】CI/CD 整合&lt;/h2>
&lt;h3 id="github-actions">GitHub Actions&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># .github/workflows/build.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build and Publish&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">tags&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s1">&amp;#39;v*&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pull_request&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">main&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 11&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 12&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 13&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Linux&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">linux&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 17&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 18&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">x86_64, aarch64]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 19&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 20&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 21&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 22&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 23&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 24&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.11&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 25&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 26&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build wheels&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 27&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">PyO3/maturin-action@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 28&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 29&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.target }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 30&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>--&lt;span class="l">release --out dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 31&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">manylinux&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">auto&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 32&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 33&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Upload wheels&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 34&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/upload-artifact@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 35&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 36&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wheels-linux-${{ matrix.target }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 37&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 38&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 39&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># macOS&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 40&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">macos&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 41&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">macos-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 42&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 43&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 44&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">x86_64, aarch64]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 45&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 46&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 47&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 48&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 49&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 50&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.11&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 51&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 52&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build wheels&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 53&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">PyO3/maturin-action@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 54&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 55&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.target }}-apple-darwin&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 56&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>--&lt;span class="l">release --out dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 57&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 58&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Upload wheels&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 59&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/upload-artifact@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 60&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 61&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wheels-macos-${{ matrix.target }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 62&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 63&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 64&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Windows&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 65&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">windows&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 66&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">windows-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 67&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 68&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 69&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 70&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 71&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 72&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.11&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 73&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 74&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build wheels&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 75&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">PyO3/maturin-action@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 76&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 77&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>--&lt;span class="l">release --out dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 78&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 79&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Upload wheels&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 80&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/upload-artifact@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 81&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 82&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wheels-windows&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 83&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 84&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 85&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 發布&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 86&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">publish&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 87&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">needs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">linux, macos, windows]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 88&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 89&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 90&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 91&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/download-artifact@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 92&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 93&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pattern&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wheels-*&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 94&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">merge-multiple&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 95&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 96&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 97&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Publish to PyPI&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 98&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">PyO3/maturin-action@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 99&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">100&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">MATURIN_PYPI_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.PYPI_API_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">101&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">102&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">upload&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">103&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>--&lt;span class="l">non-interactive --skip-existing dist/*&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="測試工作流程">測試工作流程&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># .github/workflows/test.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">main]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pull_request&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.os }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">ubuntu-latest, macos-latest, windows-latest]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;3.9&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.10&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.11&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.12&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.python-version }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install Rust&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dtolnay/rust-toolchain@stable&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install maturin&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pip install maturin pytest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build and install&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">maturin develop&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Run tests&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pytest tests/&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="進階效能優化">【進階】效能優化&lt;/h2>
&lt;h3 id="編譯優化">編譯優化&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># Cargo.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">profile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">release&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nx">lto&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;fat&amp;#34;&lt;/span> &lt;span class="c"># 最佳化連結（較慢編譯）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="nx">codegen-units&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="c"># 單一編譯單元（更好優化）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nx">panic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;abort&amp;#34;&lt;/span> &lt;span class="c"># 不展開 panic（較小二進位）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nx">strip&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="c"># 移除符號&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c"># 針對特定 CPU 優化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c"># RUSTFLAGS=&amp;#34;-C target-cpu=native&amp;#34; maturin build --release&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="減小二進位大小">減小二進位大小&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># Cargo.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">profile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">release&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">opt-level&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;z&amp;#34;&lt;/span> &lt;span class="c"># 優化大小而非速度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">lto&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">strip&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">panic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;abort&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c"># 使用 features 減少不需要的程式碼&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">pyo3&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.23&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">default-features&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">features&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;extension-module&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="除錯技巧">除錯技巧&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 保留除錯符號&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">maturin develop --profile dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 啟用 Rust backtrace&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nv">RUST_BACKTRACE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> python -c &lt;span class="s2">&amp;#34;import my_module; my_module.buggy_function()&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 lldb/gdb&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">lldb python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="o">(&lt;/span>lldb&lt;span class="o">)&lt;/span> run -c &lt;span class="s2">&amp;#34;import my_module; my_module.buggy_function()&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="常見問題疑難排解">【常見問題】疑難排解&lt;/h2>
&lt;h3 id="編譯錯誤">編譯錯誤&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">問題：找不到 Python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">解決：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">1. 確認 Python 在 PATH 中
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">2. 使用 --interpreter 指定路徑
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> maturin develop --interpreter /usr/bin/python3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">問題：連結錯誤（Linux）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">解決：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">1. 安裝 python3-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> sudo apt install python3-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">2. 安裝 build-essential
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> sudo apt install build-essential
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">問題：找不到 PyO3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">解決：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">1. 確認 Cargo.toml 中有正確的 pyo3 依賴
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">2. 執行 cargo update&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="執行時錯誤">執行時錯誤&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">問題：ImportError: undefined symbol
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">原因：模組和 Python 版本不匹配
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">解決：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">1. 重新建構
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> maturin develop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">2. 確認使用正確的 Python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> which python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">問題：記憶體錯誤 / Segfault
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">原因：通常是 unsafe 程式碼或 GIL 問題
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">解決：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">1. 檢查 unsafe 區塊
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">2. 確認正確使用 py.allow_threads()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">3. 使用 RUST_BACKTRACE=1 獲取堆疊追蹤&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="完整範例專案">完整範例專案&lt;/h2>
&lt;h3 id="專案結構">專案結構&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">fibonacci_rs/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── Cargo.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">│ └── lib.rs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── python/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ └── fibonacci_rs/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ └── py.typed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│ └── test_fib.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">└── README.md&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="完整程式碼">完整程式碼&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># Cargo.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">package&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;fibonacci_rs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">edition&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;2021&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">lib&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;fibonacci_rs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">crate-type&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;cdylib&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">pyo3&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.23&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">features&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;extension-module&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">profile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">release&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nx">lto&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nx">codegen-units&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;maturin&amp;gt;=1.5,&amp;lt;2.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;maturin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;fibonacci-rs&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">description&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Fast Fibonacci implementation in Rust&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">requires-python&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">maturin&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">python-source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="nx">module-name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;fibonacci_rs._core&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">// src/lib.rs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pyo3&lt;/span>::&lt;span class="n">prelude&lt;/span>::&lt;span class="o">*&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="sd">/// 計算 Fibonacci 數列第 n 項
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="sd">&lt;/span>&lt;span class="cp">#[pyfunction]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">fib&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>: &lt;span class="kt">u64&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kt">u64&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="o">..=&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tmp&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tmp&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="sd">/// 計算 Fibonacci 數列前 n 項
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="sd">&lt;/span>&lt;span class="cp">#[pyfunction]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">fib_sequence&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">py&lt;/span>: &lt;span class="nc">Python&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nb">&amp;#39;_&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>: &lt;span class="kt">usize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">u64&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">py&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">allow_threads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">seq&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Vec&lt;/span>::&lt;span class="n">with_capacity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="k">u64&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">seq&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tmp&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tmp&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">seq&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">})&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cp">#[pymodule]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">_core&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">Bound&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nb">&amp;#39;_&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PyModule&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">PyResult&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="fm">wrap_pyfunction!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fib&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">add_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="fm">wrap_pyfunction!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fib_sequence&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Ok&lt;/span>&lt;span class="p">(())&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># python/fibonacci_rs/__init__.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">._core&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">fib&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fib_sequence&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="n">__all__&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;fib&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;fib_sequence&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="n">__version__&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;0.1.0&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># tests/test_fib.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">fibonacci_rs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_fib_zero&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">fibonacci_rs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fib&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_fib_one&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">fibonacci_rs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fib&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_fib_ten&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">fibonacci_rs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fib&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">55&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_fib_sequence&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="n">seq&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">fibonacci_rs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fib_sequence&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">seq&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">21&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">34&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="思考題">思考題&lt;/h2>
&lt;ol>
&lt;li>為什麼 Maturin 使用 &lt;code>cdylib&lt;/code> crate type？與 &lt;code>rlib&lt;/code> 有什麼差異？&lt;/li>
&lt;li>abi3 功能如何減少需要建構的 wheel 數量？有什麼限制？&lt;/li>
&lt;li>在 CI 中建構跨平台 wheel 時，最大的挑戰是什麼？&lt;/li>
&lt;/ol>
&lt;h2 id="實作練習">實作練習&lt;/h2>
&lt;ol>
&lt;li>建立一個新的 Maturin 專案，實現一個簡單的字串處理函式&lt;/li>
&lt;li>設定 GitHub Actions 自動建構並發布到 TestPyPI&lt;/li>
&lt;li>比較 &lt;code>maturin develop&lt;/code> 和 &lt;code>maturin develop --release&lt;/code> 的編譯時間和執行效能&lt;/li>
&lt;/ol>
&lt;h2 id="延伸閱讀">延伸閱讀&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.maturin.rs/">Maturin 官方文件&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/PyO3/maturin">Maturin GitHub&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/PyO3/maturin-action">PyO3/maturin-action&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://packaging.python.org/">Python Packaging User Guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;em>上一章：&lt;a href="https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/pyo3-basics/" data-link-title="5.2 PyO3 基礎" data-link-desc="使用 PyO3 建立 Rust 與 Python 的綁定">PyO3 基礎&lt;/a>&lt;/em>
&lt;em>下一章：&lt;a href="https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/real-world-examples/" data-link-title="5.4 實戰案例分析" data-link-desc="分析知名 Python 專案如何使用 Rust">實戰案例分析&lt;/a>&lt;/em>&lt;/p></description><content:encoded><![CDATA[<p>本章介紹 Maturin，Rust Python 套件的建構工具。</p>
<h2 id="本章目標">本章目標</h2>
<p>學完本章後，你將能夠：</p>
<ol>
<li>設定 Maturin 專案</li>
<li>使用 maturin develop 快速迭代</li>
<li>建構跨平台 wheel 並發布到 PyPI</li>
</ol>
<hr>
<h2 id="原理層maturin-是什麼">【原理層】Maturin 是什麼？</h2>
<h3 id="建構工具的角色">建構工具的角色</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">傳統 Python 擴展建構：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── setuptools + setup.py
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 複雜的編譯器設定
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 手動處理連結問題
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── 跨平台建構困難
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">Maturin 提供：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 一鍵建構 Rust Python 套件
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 自動處理 PyO3 設定
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 跨平台 wheel 生成
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── 與 pyproject.toml 整合
</span></span><span class="line"><span class="ln">12</span><span class="cl">└── 開發模式快速迭代</span></span></code></pre></div><h3 id="支援的專案類型">支援的專案類型</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Maturin 支援：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── pyo3：純 Rust 擴展（最常用）
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── cffi：生成 cffi 綁定
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── uniffi：跨語言綁定
</span></span><span class="line"><span class="ln">5</span><span class="cl">└── bin：Rust 二進位程式打包</span></span></code></pre></div><hr>
<h2 id="設計層專案設定">【設計層】專案設定</h2>
<h3 id="安裝-maturin">安裝 Maturin</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 使用 pip</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install maturin
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 使用 pipx（推薦，隔離環境）</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">pipx install maturin
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 使用 cargo</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">cargo install maturin
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 驗證安裝</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">maturin --version</span></span></code></pre></div><h3 id="建立新專案">建立新專案</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 互動式建立</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin new my_rust_module
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nb">cd</span> my_rust_module
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 或者指定綁定類型</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">maturin new my_rust_module --bindings pyo3
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># 專案結構</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">my_rust_module/
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── Cargo.toml
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln">13</span><span class="cl">│   └── lib.rs
</span></span><span class="line"><span class="ln">14</span><span class="cl">└── python/
</span></span><span class="line"><span class="ln">15</span><span class="cl">    └── my_rust_module/
</span></span><span class="line"><span class="ln">16</span><span class="cl">        └── __init__.py  <span class="c1"># 選用</span></span></span></code></pre></div><h3 id="pyprojecttoml-設定">pyproject.toml 設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;maturin&gt;=1.5,&lt;2.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;maturin&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-rust-module&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;A Python module written in Rust&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.8&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">classifiers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s2">&#34;Programming Language :: Rust&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: Implementation :: CPython&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="s2">&#34;Programming Language :: Python :: Implementation :: PyPy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pytest&#34;</span><span class="p">,</span> <span class="s2">&#34;hypothesis&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">maturin</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c"># 模組名稱（如果與 crate 名稱不同）</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nx">module-name</span> <span class="p">=</span> <span class="s2">&#34;my_rust_module&#34;</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c"># Python 原始碼目錄（如果有純 Python 程式碼）</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="nx">python-source</span> <span class="p">=</span> <span class="s2">&#34;python&#34;</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c"># 功能標誌</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;pyo3/extension-module&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c"># 啟用 abi3</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c"># features = [&#34;pyo3/extension-module&#34;, &#34;pyo3/abi3-py38&#34;]</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="c"># 排除不需要的檔案</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="nx">exclude</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;tests/*&#34;</span><span class="p">,</span> <span class="s2">&#34;benches/*&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c"># 建構設定</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c"># strip = true  # 移除除錯符號（減小檔案大小）</span></span></span></code></pre></div><h3 id="cargotoml-設定">Cargo.toml 設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my_rust_module&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2021&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my_rust_module&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">pyo3</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.23&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;extension-module&#34;</span><span class="p">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span>          <span class="c"># Link-Time Optimization</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">codegen-units</span> <span class="p">=</span> <span class="mi">1</span>   <span class="c"># 更好的優化</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">strip</span> <span class="p">=</span> <span class="kc">true</span>        <span class="c"># 移除除錯符號</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">dev</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">opt-level</span> <span class="p">=</span> <span class="mi">0</span>       <span class="c"># 快速編譯</span></span></span></code></pre></div><hr>
<h2 id="實作層開發流程">【實作層】開發流程</h2>
<h3 id="maturin-develop">maturin develop</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 開發模式：編譯並安裝到當前虛擬環境</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin develop
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 使用 release 模式（較慢但更快的執行時效能）</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">maturin develop --release
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 指定功能</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">maturin develop --features <span class="s2">&#34;some-feature&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 在其他虛擬環境中安裝</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">maturin develop --target-dir target -E /path/to/venv
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 使用後立即測試</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">maturin develop <span class="o">&amp;&amp;</span> python -c <span class="s2">&#34;import my_rust_module; print(my_rust_module.add(1, 2))&#34;</span></span></span></code></pre></div><h3 id="完整開發循環">完整開發循環</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. 建立虛擬環境</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">python -m venv .venv
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nb">source</span> .venv/bin/activate  <span class="c1"># Linux/macOS</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># .venv\Scripts\activate   # Windows</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 2. 安裝開發依賴</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">pip install maturin pytest
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 3. 開發循環</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">while</span> editing:
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="c1"># 編輯 Rust 程式碼</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    vim src/lib.rs
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="c1"># 建構並安裝</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    maturin develop
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="c1"># 執行測試</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    pytest tests/
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"># 4. 準備發布</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">maturin build --release</span></span></code></pre></div><h3 id="混合-pythonrust-專案">混合 Python/Rust 專案</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">專案結構：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">my_package/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── Cargo.toml
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">│   └── lib.rs              # Rust 程式碼
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">└── python/
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    └── my_package/
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        ├── __init__.py     # 匯入 Rust 模組
</span></span><span class="line"><span class="ln">10</span><span class="cl">        ├── utils.py        # 純 Python 程式碼
</span></span><span class="line"><span class="ln">11</span><span class="cl">        └── py.typed        # 型別標記</span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># python/my_package/__init__.py</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">from</span> <span class="nn">.my_package</span> <span class="kn">import</span> <span class="o">*</span>  <span class="c1"># 從 Rust 模組匯入</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">helper_function</span>  <span class="c1"># 純 Python</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">__version__</span> <span class="o">=</span> <span class="s2">&#34;0.1.0&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">maturin</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">python-source</span> <span class="p">=</span> <span class="s2">&#34;python&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">module-name</span> <span class="p">=</span> <span class="s2">&#34;my_package.my_package&#34;</span></span></span></code></pre></div><hr>
<h2 id="實作層建構與發布">【實作層】建構與發布</h2>
<h3 id="maturin-build">maturin build</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 建構 wheel</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin build
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># Release 模式</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">maturin build --release
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 指定 Python 版本</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">maturin build --interpreter python3.11
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 多個 Python 版本</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">maturin build --interpreter python3.10 python3.11 python3.12
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 使用 abi3（穩定 ABI）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">maturin build --release --features pyo3/abi3-py38
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># 建構結果位置</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">ls target/wheels/
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># my_rust_module-0.1.0-cp311-cp311-linux_x86_64.whl</span></span></span></code></pre></div><h3 id="跨平台建構">跨平台建構</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 使用 Docker 建構 manylinux wheel</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin build --release --manylinux <span class="m">2014</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 常用的 manylinux 版本</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># manylinux1:   CentOS 5 (非常老舊)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># manylinux2010: CentOS 6</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># manylinux2014: CentOS 7 (推薦)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># manylinux_2_28: Debian 9 / Ubuntu 18.04</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 使用 zig 進行交叉編譯</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">pip install ziglang
</span></span><span class="line"><span class="ln">12</span><span class="cl">maturin build --release --target x86_64-unknown-linux-gnu --zig
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># 常見目標平台</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># x86_64-unknown-linux-gnu</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># aarch64-unknown-linux-gnu</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"># x86_64-apple-darwin</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># aarch64-apple-darwin</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"># x86_64-pc-windows-msvc</span></span></span></code></pre></div><h3 id="發布到-pypi">發布到 PyPI</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 發布到 TestPyPI（測試）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">maturin publish --repository testpypi
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 發布到 PyPI</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">maturin publish
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 使用 API token</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">maturin publish --username __token__ --password &lt;your-pypi-token&gt;
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 或者設定環境變數</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nb">export</span> <span class="nv">MATURIN_PYPI_TOKEN</span><span class="o">=</span>&lt;your-pypi-token&gt;
</span></span><span class="line"><span class="ln">12</span><span class="cl">maturin publish</span></span></code></pre></div><hr>
<h2 id="實作層cicd-整合">【實作層】CI/CD 整合</h2>
<h3 id="github-actions">GitHub Actions</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">  1</span><span class="cl"><span class="c"># .github/workflows/build.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build and Publish</span><span class="w">
</span></span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="w">    </span><span class="nt">tags</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="w">      </span>- <span class="s1">&#39;v*&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="w">      </span>- <span class="l">main</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="w">  </span><span class="c"># Linux</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="w">  </span><span class="nt">linux</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="w">        </span><span class="nt">target</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">x86_64, aarch64]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;3.11&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">PyO3/maturin-action@v1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="w">          </span><span class="nt">target</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.target }}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="w">          </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span>--<span class="l">release --out dist</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="w">          </span><span class="nt">manylinux</span><span class="p">:</span><span class="w"> </span><span class="l">auto</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wheels-linux-${{ matrix.target }}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="w">  </span><span class="c"># macOS</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="w">  </span><span class="nt">macos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">macos-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="w">        </span><span class="nt">target</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">x86_64, aarch64]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;3.11&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">PyO3/maturin-action@v1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="w">          </span><span class="nt">target</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.target }}-apple-darwin</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="w">          </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span>--<span class="l">release --out dist</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wheels-macos-${{ matrix.target }}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="w">  </span><span class="c"># Windows</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="w">  </span><span class="nt">windows</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">windows-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;3.11&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">PyO3/maturin-action@v1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="w">          </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span>--<span class="l">release --out dist</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wheels-windows</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="w">  </span><span class="c"># 發布</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="w">  </span><span class="nt">publish</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">linux, macos, windows]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">startsWith(github.ref, &#39;refs/tags/&#39;)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="w">          </span><span class="nt">pattern</span><span class="p">:</span><span class="w"> </span><span class="l">wheels-*</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="w">          </span><span class="nt">merge-multiple</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish to PyPI</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">PyO3/maturin-action@v1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="w">          </span><span class="nt">MATURIN_PYPI_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.PYPI_API_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="w">          </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">upload</span><span class="w">
</span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="w">          </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span>--<span class="l">non-interactive --skip-existing dist/*</span></span></span></code></pre></div><h3 id="測試工作流程">測試工作流程</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/test.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Test</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.os }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="nt">os</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ubuntu-latest, macos-latest, windows-latest]</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;3.9&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;3.10&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;3.11&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;3.12&#39;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.python-version }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install Rust</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">dtolnay/rust-toolchain@stable</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install maturin</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pip install maturin pytest</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build and install</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">maturin develop</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run tests</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pytest tests/</span></span></span></code></pre></div><hr>
<h2 id="進階效能優化">【進階】效能優化</h2>
<h3 id="編譯優化">編譯優化</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># Cargo.toml</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="s2">&#34;fat&#34;</span>         <span class="c"># 最佳化連結（較慢編譯）</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">codegen-units</span> <span class="p">=</span> <span class="mi">1</span>   <span class="c"># 單一編譯單元（更好優化）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">panic</span> <span class="p">=</span> <span class="s2">&#34;abort&#34;</span>     <span class="c"># 不展開 panic（較小二進位）</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">strip</span> <span class="p">=</span> <span class="kc">true</span>        <span class="c"># 移除符號</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c"># 針對特定 CPU 優化</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c"># RUSTFLAGS=&#34;-C target-cpu=native&#34; maturin build --release</span></span></span></code></pre></div><h3 id="減小二進位大小">減小二進位大小</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># Cargo.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">opt-level</span> <span class="p">=</span> <span class="s2">&#34;z&#34;</span>     <span class="c"># 優化大小而非速度</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">strip</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">panic</span> <span class="p">=</span> <span class="s2">&#34;abort&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c"># 使用 features 減少不需要的程式碼</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">pyo3</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.23&#34;</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;extension-module&#34;</span><span class="p">]</span> <span class="p">}</span></span></span></code></pre></div><h3 id="除錯技巧">除錯技巧</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 保留除錯符號</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">maturin develop --profile dev
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 啟用 Rust backtrace</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nv">RUST_BACKTRACE</span><span class="o">=</span><span class="m">1</span> python -c <span class="s2">&#34;import my_module; my_module.buggy_function()&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 使用 lldb/gdb</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">lldb python
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="o">(</span>lldb<span class="o">)</span> run -c <span class="s2">&#34;import my_module; my_module.buggy_function()&#34;</span></span></span></code></pre></div><hr>
<h2 id="常見問題疑難排解">【常見問題】疑難排解</h2>
<h3 id="編譯錯誤">編譯錯誤</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">問題：找不到 Python
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">解決：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">1. 確認 Python 在 PATH 中
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">2. 使用 --interpreter 指定路徑
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">   maturin develop --interpreter /usr/bin/python3.11
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">問題：連結錯誤（Linux）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">解決：
</span></span><span class="line"><span class="ln">11</span><span class="cl">1. 安裝 python3-dev
</span></span><span class="line"><span class="ln">12</span><span class="cl">   sudo apt install python3-dev
</span></span><span class="line"><span class="ln">13</span><span class="cl">2. 安裝 build-essential
</span></span><span class="line"><span class="ln">14</span><span class="cl">   sudo apt install build-essential
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">問題：找不到 PyO3
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">解決：
</span></span><span class="line"><span class="ln">19</span><span class="cl">1. 確認 Cargo.toml 中有正確的 pyo3 依賴
</span></span><span class="line"><span class="ln">20</span><span class="cl">2. 執行 cargo update</span></span></code></pre></div><h3 id="執行時錯誤">執行時錯誤</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">問題：ImportError: undefined symbol
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">原因：模組和 Python 版本不匹配
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">解決：
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">1. 重新建構
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">   maturin develop
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">2. 確認使用正確的 Python
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   which python
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">問題：記憶體錯誤 / Segfault
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">原因：通常是 unsafe 程式碼或 GIL 問題
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">解決：
</span></span><span class="line"><span class="ln">16</span><span class="cl">1. 檢查 unsafe 區塊
</span></span><span class="line"><span class="ln">17</span><span class="cl">2. 確認正確使用 py.allow_threads()
</span></span><span class="line"><span class="ln">18</span><span class="cl">3. 使用 RUST_BACKTRACE=1 獲取堆疊追蹤</span></span></code></pre></div><hr>
<h2 id="完整範例專案">完整範例專案</h2>
<h3 id="專案結構">專案結構</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">fibonacci_rs/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── Cargo.toml
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│   └── lib.rs
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── python/
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│   └── fibonacci_rs/
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">│       ├── __init__.py
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│       └── py.typed
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── tests/
</span></span><span class="line"><span class="ln">11</span><span class="cl">│   └── test_fib.py
</span></span><span class="line"><span class="ln">12</span><span class="cl">└── README.md</span></span></code></pre></div><h3 id="完整程式碼">完整程式碼</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># Cargo.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">package</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;fibonacci_rs&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">edition</span> <span class="p">=</span> <span class="s2">&#34;2021&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">[</span><span class="nx">lib</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;fibonacci_rs&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">crate-type</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;cdylib&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">pyo3</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.23&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;extension-module&#34;</span><span class="p">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">codegen-units</span> <span class="p">=</span> <span class="mi">1</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;maturin&gt;=1.5,&lt;2.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;maturin&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;fibonacci-rs&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Fast Fibonacci implementation in Rust&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">&#34;&gt;=3.8&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">maturin</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">python-source</span> <span class="p">=</span> <span class="s2">&#34;python&#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">module-name</span> <span class="p">=</span> <span class="s2">&#34;fibonacci_rs._core&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// src/lib.rs
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">use</span><span class="w"> </span><span class="n">pyo3</span>::<span class="n">prelude</span>::<span class="o">*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="sd">/// 計算 Fibonacci 數列第 n 項
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">fib</span><span class="p">(</span><span class="n">n</span>: <span class="kt">u64</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">u64</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">n</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="k">u64</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="k">u64</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">2</span><span class="o">..=</span><span class="n">n</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="n">tmp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">tmp</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="n">b</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w"></span><span class="sd">/// 計算 Fibonacci 數列前 n 項
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="sd"></span><span class="cp">#[pyfunction]</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">fib_sequence</span><span class="p">(</span><span class="n">py</span>: <span class="nc">Python</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="n">n</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">u64</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="n">py</span><span class="p">.</span><span class="n">allow_threads</span><span class="p">(</span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">seq</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="n">n</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="k">mut</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="mi">0</span><span class="k">u64</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="k">u64</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">n</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">            </span><span class="n">seq</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">a</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">            </span><span class="kd">let</span><span class="w"> </span><span class="n">tmp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">            </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">            </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">tmp</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="n">seq</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">    </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w"></span><span class="cp">#[pymodule]</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">_core</span><span class="p">(</span><span class="n">m</span>: <span class="kp">&amp;</span><span class="nc">Bound</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="p">,</span><span class="w"> </span><span class="n">PyModule</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">PyResult</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">fib</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">    </span><span class="n">m</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="fm">wrap_pyfunction!</span><span class="p">(</span><span class="n">fib_sequence</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">    </span><span class="nb">Ok</span><span class="p">(())</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># python/fibonacci_rs/__init__.py</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">from</span> <span class="nn">._core</span> <span class="kn">import</span> <span class="n">fib</span><span class="p">,</span> <span class="n">fib_sequence</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;fib&#34;</span><span class="p">,</span> <span class="s2">&#34;fib_sequence&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">__version__</span> <span class="o">=</span> <span class="s2">&#34;0.1.0&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># tests/test_fib.py</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">fibonacci_rs</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">def</span> <span class="nf">test_fib_zero</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">assert</span> <span class="n">fibonacci_rs</span><span class="o">.</span><span class="n">fib</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">def</span> <span class="nf">test_fib_one</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="k">assert</span> <span class="n">fibonacci_rs</span><span class="o">.</span><span class="n">fib</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">def</span> <span class="nf">test_fib_ten</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">assert</span> <span class="n">fibonacci_rs</span><span class="o">.</span><span class="n">fib</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="o">==</span> <span class="mi">55</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">def</span> <span class="nf">test_fib_sequence</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">seq</span> <span class="o">=</span> <span class="n">fibonacci_rs</span><span class="o">.</span><span class="n">fib_sequence</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">assert</span> <span class="n">seq</span> <span class="o">==</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">34</span><span class="p">]</span></span></span></code></pre></div><hr>
<h2 id="思考題">思考題</h2>
<ol>
<li>為什麼 Maturin 使用 <code>cdylib</code> crate type？與 <code>rlib</code> 有什麼差異？</li>
<li>abi3 功能如何減少需要建構的 wheel 數量？有什麼限制？</li>
<li>在 CI 中建構跨平台 wheel 時，最大的挑戰是什麼？</li>
</ol>
<h2 id="實作練習">實作練習</h2>
<ol>
<li>建立一個新的 Maturin 專案，實現一個簡單的字串處理函式</li>
<li>設定 GitHub Actions 自動建構並發布到 TestPyPI</li>
<li>比較 <code>maturin develop</code> 和 <code>maturin develop --release</code> 的編譯時間和執行效能</li>
</ol>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://www.maturin.rs/">Maturin 官方文件</a></li>
<li><a href="https://github.com/PyO3/maturin">Maturin GitHub</a></li>
<li><a href="https://github.com/PyO3/maturin-action">PyO3/maturin-action</a></li>
<li><a href="https://packaging.python.org/">Python Packaging User Guide</a></li>
</ul>
<hr>
<p><em>上一章：<a href="/blog/python-advanced/06-rust-extensions/pyo3-basics/" data-link-title="5.2 PyO3 基礎" data-link-desc="使用 PyO3 建立 Rust 與 Python 的綁定">PyO3 基礎</a></em>
<em>下一章：<a href="/blog/python-advanced/06-rust-extensions/real-world-examples/" data-link-title="5.4 實戰案例分析" data-link-desc="分析知名 Python 專案如何使用 Rust">實戰案例分析</a></em></p>
]]></content:encoded></item><item><title>6.3 發布到 PyPI</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/distribution/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/distribution/</guid><description>&lt;p>本章介紹如何將套件發布到 PyPI。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>學完本章後，你將能夠：&lt;/p>
&lt;ol>
&lt;li>建構 sdist 和 wheel&lt;/li>
&lt;li>使用 twine 上傳到 PyPI&lt;/li>
&lt;li>設定 CI/CD 自動發布&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="原理層套件分發格式">【原理層】套件分發格式&lt;/h2>
&lt;h3 id="sdist-vs-wheel">sdist vs wheel&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">sdist（Source Distribution）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 格式：.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 內容：原始碼 + pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 安裝時需要建構
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── 可能需要編譯器（C 擴展）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">└── 作為備用方案
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">wheel（Built Distribution）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 格式：.whl（實際上是 zip）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 內容：已編譯的套件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">├── 安裝時直接解壓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── 不需要編譯器
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">└── 安裝速度快&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="wheel-命名規則">wheel 命名規則&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">範例：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">my_package-1.0.0-py3-none-any.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── my_package: 套件名稱
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── 1.0.0: 版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── py3: Python 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── none: 無特定 ABI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">└── any: 任何平台
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── numpy: 套件名稱
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">├── 1.26.0: 版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">├── cp311: CPython 3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">├── cp311: CPython 3.11 ABI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">└── manylinux_2_17_x86_64: Linux x86_64&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="常見的-platform-tag">常見的 platform tag&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">純 Python：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── py3-none-any（Python 3，任何平台）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── py2.py3-none-any（Python 2 和 3）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">有 C 擴展：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── cp311-cp311-manylinux_2_17_x86_64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── cp311-cp311-macosx_11_0_arm64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── cp311-cp311-win_amd64
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">使用 abi3（穩定 ABI）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">└── cp38-abi3-manylinux_2_17_x86_64&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層建構套件">【實作層】建構套件&lt;/h2>
&lt;h3 id="使用-build-工具">使用 build 工具&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">pip install build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構 sdist 和 wheel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">python -m build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只建構 wheel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">python -m build --wheel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只建構 sdist&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">python -m build --sdist
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構結果&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">ls dist/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c1"># my_package-1.0.0.tar.gz (sdist)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="c1"># my_package-1.0.0-py3-none-any.whl (wheel)&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="檢查建構結果">檢查建構結果&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 twine&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">pip install twine
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 檢查套件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">twine check dist/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 檢查 wheel 內容&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">unzip -l dist/my_package-1.0.0-py3-none-any.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或使用 wheel 工具&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">pip install wheel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">wheel unpack dist/my_package-1.0.0-py3-none-any.whl&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="測試安裝">測試安裝&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在新的虛擬環境中測試&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">python -m venv test_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> test_env/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 從本地檔案安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">pip install dist/my_package-1.0.0-py3-none-any.whl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># 測試匯入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">python -c &lt;span class="s2">&amp;#34;import my_package; print(my_package.__version__)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c1"># 清理&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">deactivate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">rm -rf test_env&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層發布到-pypi">【實作層】發布到 PyPI&lt;/h2>
&lt;h3 id="註冊帳號">註冊帳號&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">1. 前往 https://pypi.org/account/register/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">2. 建立帳號並驗證 email
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">3. 啟用雙因素認證（強烈建議）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">4. 建立 API Token
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> - Account Settings → API tokens → Add API token
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> - Scope: Entire account（首次）或特定專案
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl"> - 複製 token（只會顯示一次）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="設定認證">設定認證&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 方法 1：使用 .pypirc 檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">cat &amp;gt; ~/.pypirc &lt;span class="s">&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="s">[pypi]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="s">username = __token__
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="s">password = pypi-xxxxxxxxxxxx
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="s">[testpypi]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="s">username = __token__
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="s">password = pypi-xxxxxxxxxxxx
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># 設定檔案權限&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">chmod &lt;span class="m">600&lt;/span> ~/.pypirc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c1"># 方法 2：使用環境變數&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">TWINE_USERNAME&lt;/span>&lt;span class="o">=&lt;/span>__token__
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">TWINE_PASSWORD&lt;/span>&lt;span class="o">=&lt;/span>pypi-xxxxxxxxxxxx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="c1"># 方法 3：使用 keyring&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">pip install keyring
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">keyring &lt;span class="nb">set&lt;/span> https://upload.pypi.org/legacy/ __token__&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="發布到-testpypi測試">發布到 TestPyPI（測試）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># TestPyPI 是 PyPI 的測試環境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1"># 用於在正式發布前測試&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 註冊 TestPyPI 帳號&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># https://test.pypi.org/account/register/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 上傳到 TestPyPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">twine upload --repository testpypi dist/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 從 TestPyPI 安裝測試&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">pip install --index-url https://test.pypi.org/simple/ my-package&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="發布到-pypi">發布到 PyPI&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 確認一切就緒&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">twine check dist/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 上傳&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">twine upload dist/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或指定檔案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">twine upload dist/my_package-1.0.0*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 驗證&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">pip install my-package&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="使用其他工具發布">使用其他工具發布&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># Poetry&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">poetry publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># Hatch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">hatch publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># Flit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">flit publish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># Maturin（Rust 擴展）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">maturin publish&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層版本管理">【實作層】版本管理&lt;/h2>
&lt;h3 id="語義化版本">語義化版本&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">MAJOR.MINOR.PATCH
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">範例：1.2.3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── MAJOR (1): 不相容的 API 變更
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── MINOR (2): 新增功能，向後相容
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">└── PATCH (3): 修復 bug，向後相容
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">預發布版本：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 1.0.0a1 (alpha)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 1.0.0b1 (beta)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">├── 1.0.0rc1 (release candidate)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">開發版本：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">├── 1.0.0.dev1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">├── 1.0.0.post1 (post-release)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="pep-440-版本格式">PEP 440 版本格式&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">合法的版本號：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 1.0.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 1.0.0a1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── 1.0.0b2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── 1.0.0rc1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── 1.0.0.dev1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── 1.0.0.post1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">└── 1.0.0+local
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">不合法（會被正規化）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── 1.0.0-alpha1 → 1.0.0a1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">├── v1.0.0 → 1.0.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">└── 1.0.0.RELEASE → 1.0.0&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="自動版本管理">自動版本管理&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 setuptools-scm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1"># 從 git tag 自動產生版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">pip install setuptools-scm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># pyproject.toml 設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># [tool.setuptools_scm]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建立 tag&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">git tag v1.0.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">git push --tags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建構（版本自動從 tag 取得）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">python -m build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 hatch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">hatch version minor &lt;span class="c1"># 1.0.0 → 1.1.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">hatch version patch &lt;span class="c1"># 1.1.0 → 1.1.1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 poetry&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">poetry version minor
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">poetry version patch&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層cicd-自動發布">【實作層】CI/CD 自動發布&lt;/h2>
&lt;h3 id="github-actions">GitHub Actions&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># .github/workflows/publish.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Publish to PyPI&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">release&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">types&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">published]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Set up Python&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.11&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install build tools&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pip install build twine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build package&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">python -m build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Check package&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">twine check dist/*&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Upload artifacts&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/upload-artifact@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">publish-testpypi&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">needs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">testpypi&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">permissions&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">id-token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">write &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 用於 Trusted Publishing&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/download-artifact@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Publish to TestPyPI&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pypa/gh-action-pypi-publish@release/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">repository-url&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">https://test.pypi.org/legacy/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">publish-pypi&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">needs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">build, publish-testpypi]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pypi&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">permissions&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">id-token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">write&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/download-artifact@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dist/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Publish to PyPI&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pypa/gh-action-pypi-publish@release/v1&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="trusted-publishing推薦">Trusted Publishing（推薦）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">Trusted Publishing：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 不需要儲存 API token
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 使用 OIDC（OpenID Connect）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 更安全的發布方式
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">└── GitHub Actions 原生支援
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">設定步驟：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">1. 在 PyPI 專案設定中新增 Publisher
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">2. 選擇 GitHub Actions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">3. 填入 repository 和 workflow 資訊
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">4. 在 workflow 中使用 id-token: write&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 PyPI 設定 Trusted Publisher：&lt;/p></description><content:encoded><![CDATA[<p>本章介紹如何將套件發布到 PyPI。</p>
<h2 id="本章目標">本章目標</h2>
<p>學完本章後，你將能夠：</p>
<ol>
<li>建構 sdist 和 wheel</li>
<li>使用 twine 上傳到 PyPI</li>
<li>設定 CI/CD 自動發布</li>
</ol>
<hr>
<h2 id="原理層套件分發格式">【原理層】套件分發格式</h2>
<h3 id="sdist-vs-wheel">sdist vs wheel</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">sdist（Source Distribution）：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 格式：.tar.gz
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 內容：原始碼 + pyproject.toml
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 安裝時需要建構
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── 可能需要編譯器（C 擴展）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">└── 作為備用方案
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">wheel（Built Distribution）：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 格式：.whl（實際上是 zip）
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 內容：已編譯的套件
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── 安裝時直接解壓
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── 不需要編譯器
</span></span><span class="line"><span class="ln">13</span><span class="cl">└── 安裝速度快</span></span></code></pre></div><h3 id="wheel-命名規則">wheel 命名規則</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">範例：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">my_package-1.0.0-py3-none-any.whl
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── my_package: 套件名稱
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── 1.0.0: 版本
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── py3: Python 3
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── none: 無特定 ABI
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">└── any: 任何平台
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.whl
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── numpy: 套件名稱
</span></span><span class="line"><span class="ln">13</span><span class="cl">├── 1.26.0: 版本
</span></span><span class="line"><span class="ln">14</span><span class="cl">├── cp311: CPython 3.11
</span></span><span class="line"><span class="ln">15</span><span class="cl">├── cp311: CPython 3.11 ABI
</span></span><span class="line"><span class="ln">16</span><span class="cl">└── manylinux_2_17_x86_64: Linux x86_64</span></span></code></pre></div><h3 id="常見的-platform-tag">常見的 platform tag</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">純 Python：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── py3-none-any（Python 3，任何平台）
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── py2.py3-none-any（Python 2 和 3）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">有 C 擴展：
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── cp311-cp311-manylinux_2_17_x86_64
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── cp311-cp311-macosx_11_0_arm64
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── cp311-cp311-win_amd64
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">使用 abi3（穩定 ABI）：
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── cp38-abi3-manylinux_2_17_x86_64</span></span></code></pre></div><hr>
<h2 id="實作層建構套件">【實作層】建構套件</h2>
<h3 id="使用-build-工具">使用 build 工具</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝 build</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install build
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 建構 sdist 和 wheel</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">python -m build
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 只建構 wheel</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">python -m build --wheel
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 只建構 sdist</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">python -m build --sdist
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 建構結果</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">ls dist/
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># my_package-1.0.0.tar.gz     (sdist)</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># my_package-1.0.0-py3-none-any.whl  (wheel)</span></span></span></code></pre></div><h3 id="檢查建構結果">檢查建構結果</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 安裝 twine</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">pip install twine
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 檢查套件</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">twine check dist/*
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 檢查 wheel 內容</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">unzip -l dist/my_package-1.0.0-py3-none-any.whl
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 或使用 wheel 工具</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">pip install wheel
</span></span><span class="line"><span class="ln">12</span><span class="cl">wheel unpack dist/my_package-1.0.0-py3-none-any.whl</span></span></code></pre></div><h3 id="測試安裝">測試安裝</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 在新的虛擬環境中測試</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">python -m venv test_env
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nb">source</span> test_env/bin/activate
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 從本地檔案安裝</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">pip install dist/my_package-1.0.0-py3-none-any.whl
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># 測試匯入</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">python -c <span class="s2">&#34;import my_package; print(my_package.__version__)&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 清理</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">deactivate
</span></span><span class="line"><span class="ln">13</span><span class="cl">rm -rf test_env</span></span></code></pre></div><hr>
<h2 id="實作層發布到-pypi">【實作層】發布到 PyPI</h2>
<h3 id="註冊帳號">註冊帳號</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">1. 前往 https://pypi.org/account/register/
</span></span><span class="line"><span class="ln">2</span><span class="cl">2. 建立帳號並驗證 email
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. 啟用雙因素認證（強烈建議）
</span></span><span class="line"><span class="ln">4</span><span class="cl">4. 建立 API Token
</span></span><span class="line"><span class="ln">5</span><span class="cl">   - Account Settings → API tokens → Add API token
</span></span><span class="line"><span class="ln">6</span><span class="cl">   - Scope: Entire account（首次）或特定專案
</span></span><span class="line"><span class="ln">7</span><span class="cl">   - 複製 token（只會顯示一次）</span></span></code></pre></div><h3 id="設定認證">設定認證</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 方法 1：使用 .pypirc 檔案</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">cat &gt; ~/.pypirc <span class="s">&lt;&lt; &#39;EOF&#39;
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="s">[pypi]
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="s">username = __token__
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="s">password = pypi-xxxxxxxxxxxx
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s">[testpypi]
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s">username = __token__
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s">password = pypi-xxxxxxxxxxxx
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 設定檔案權限</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">chmod <span class="m">600</span> ~/.pypirc
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># 方法 2：使用環境變數</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nb">export</span> <span class="nv">TWINE_USERNAME</span><span class="o">=</span>__token__
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nb">export</span> <span class="nv">TWINE_PASSWORD</span><span class="o">=</span>pypi-xxxxxxxxxxxx
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"># 方法 3：使用 keyring</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">pip install keyring
</span></span><span class="line"><span class="ln">21</span><span class="cl">keyring <span class="nb">set</span> https://upload.pypi.org/legacy/ __token__</span></span></code></pre></div><h3 id="發布到-testpypi測試">發布到 TestPyPI（測試）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># TestPyPI 是 PyPI 的測試環境</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"># 用於在正式發布前測試</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 註冊 TestPyPI 帳號</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># https://test.pypi.org/account/register/</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 上傳到 TestPyPI</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">twine upload --repository testpypi dist/*
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 從 TestPyPI 安裝測試</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">pip install --index-url https://test.pypi.org/simple/ my-package</span></span></code></pre></div><h3 id="發布到-pypi">發布到 PyPI</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 確認一切就緒</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">twine check dist/*
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 上傳</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">twine upload dist/*
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 或指定檔案</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">twine upload dist/my_package-1.0.0*
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 驗證</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">pip install my-package</span></span></code></pre></div><h3 id="使用其他工具發布">使用其他工具發布</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Poetry</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">poetry publish
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># Hatch</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">hatch publish
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># Flit</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">flit publish
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># Maturin（Rust 擴展）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">maturin publish</span></span></code></pre></div><hr>
<h2 id="實作層版本管理">【實作層】版本管理</h2>
<h3 id="語義化版本">語義化版本</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">MAJOR.MINOR.PATCH
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">範例：1.2.3
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── MAJOR (1): 不相容的 API 變更
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── MINOR (2): 新增功能，向後相容
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">└── PATCH (3): 修復 bug，向後相容
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">預發布版本：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 1.0.0a1  (alpha)
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 1.0.0b1  (beta)
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── 1.0.0rc1 (release candidate)
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">開發版本：
</span></span><span class="line"><span class="ln">14</span><span class="cl">├── 1.0.0.dev1
</span></span><span class="line"><span class="ln">15</span><span class="cl">├── 1.0.0.post1 (post-release)</span></span></code></pre></div><h3 id="pep-440-版本格式">PEP 440 版本格式</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">合法的版本號：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 1.0
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 1.0.0
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 1.0.0a1
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── 1.0.0b2
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── 1.0.0rc1
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── 1.0.0.dev1
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 1.0.0.post1
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">└── 1.0.0+local
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">不合法（會被正規化）：
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── 1.0.0-alpha1 → 1.0.0a1
</span></span><span class="line"><span class="ln">13</span><span class="cl">├── v1.0.0 → 1.0.0
</span></span><span class="line"><span class="ln">14</span><span class="cl">└── 1.0.0.RELEASE → 1.0.0</span></span></code></pre></div><h3 id="自動版本管理">自動版本管理</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 使用 setuptools-scm</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"># 從 git tag 自動產生版本</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 安裝</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">pip install setuptools-scm
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># pyproject.toml 設定</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># [tool.setuptools_scm]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 建立 tag</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">git tag v1.0.0
</span></span><span class="line"><span class="ln">12</span><span class="cl">git push --tags
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># 建構（版本自動從 tag 取得）</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">python -m build
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"># 使用 hatch</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">hatch version minor  <span class="c1"># 1.0.0 → 1.1.0</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">hatch version patch  <span class="c1"># 1.1.0 → 1.1.1</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"># 使用 poetry</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">poetry version minor
</span></span><span class="line"><span class="ln">23</span><span class="cl">poetry version patch</span></span></code></pre></div><hr>
<h2 id="實作層cicd-自動發布">【實作層】CI/CD 自動發布</h2>
<h3 id="github-actions">GitHub Actions</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/publish.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish to PyPI</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">release</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">published]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up Python</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;3.11&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install build tools</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pip install build twine</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build package</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">python -m build</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check package</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">twine check dist/*</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload artifacts</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">  </span><span class="nt">publish-testpypi</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">testpypi</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">    </span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">      </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write </span><span class="w"> </span><span class="c"># 用於 Trusted Publishing</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish to TestPyPI</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/gh-action-pypi-publish@release/v1</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">          </span><span class="nt">repository-url</span><span class="p">:</span><span class="w"> </span><span class="l">https://test.pypi.org/legacy/</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">  </span><span class="nt">publish-pypi</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">build, publish-testpypi]</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">pypi</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">    </span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w">      </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish to PyPI</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/gh-action-pypi-publish@release/v1</span></span></span></code></pre></div><h3 id="trusted-publishing推薦">Trusted Publishing（推薦）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">Trusted Publishing：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 不需要儲存 API token
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 使用 OIDC（OpenID Connect）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 更安全的發布方式
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── GitHub Actions 原生支援
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">設定步驟：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">1. 在 PyPI 專案設定中新增 Publisher
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">2. 選擇 GitHub Actions
</span></span><span class="line"><span class="ln">10</span><span class="cl">3. 填入 repository 和 workflow 資訊
</span></span><span class="line"><span class="ln">11</span><span class="cl">4. 在 workflow 中使用 id-token: write</span></span></code></pre></div><p>在 PyPI 設定 Trusted Publisher：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">PyPI → Your Project → Settings → Publishing
</span></span><span class="line"><span class="ln">2</span><span class="cl">
</span></span><span class="line"><span class="ln">3</span><span class="cl">新增 publisher：
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── Owner: your-github-username
</span></span><span class="line"><span class="ln">5</span><span class="cl">├── Repository: your-repo-name
</span></span><span class="line"><span class="ln">6</span><span class="cl">├── Workflow name: publish.yml
</span></span><span class="line"><span class="ln">7</span><span class="cl">└── Environment name: pypi (選填)</span></span></code></pre></div><h3 id="完整的-cicd-流程">完整的 CI/CD 流程</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/ci.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">CI/CD</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">  </span><span class="nt">release</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">published]</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.os }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="nt">os</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ubuntu-latest, macos-latest, windows-latest]</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;3.9&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;3.10&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;3.11&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;3.12&#39;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up Python</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.python-version }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install dependencies</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="sd">          pip install -e &#34;.[dev]&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run tests</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">pytest --cov</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">          </span><span class="nt">fetch-depth</span><span class="p">:</span><span class="w"> </span><span class="m">0</span><span class="w">  </span><span class="c"># 完整 history（用於 setuptools-scm）</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="sd">          pip install build
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="sd">          python -m build</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">  </span><span class="nt">publish</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">github.event_name == &#39;release&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">pypi</span><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">    </span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">      </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dist</span><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/gh-action-pypi-publish@release/v1</span></span></span></code></pre></div><hr>
<h2 id="進階多平台-wheel-建構">【進階】多平台 wheel 建構</h2>
<h3 id="使用-cibuildwheel">使用 cibuildwheel</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/wheels.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">release</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">published]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">build_wheels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheels on ${{ matrix.os }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.os }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="nt">os</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ubuntu-latest, windows-latest, macos-13, macos-14]</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/cibuildwheel@v2.17</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">          </span><span class="c"># 建構 Python 3.9-3.12</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">          </span><span class="nt">CIBW_BUILD</span><span class="p">:</span><span class="w"> </span><span class="l">cp39-* cp310-* cp311-* cp312-*</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">          </span><span class="c"># 跳過 32-bit 和 musl</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">          </span><span class="nt">CIBW_SKIP</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;*-win32 *-manylinux_i686 *-musllinux*&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">          </span><span class="c"># 測試命令</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">          </span><span class="nt">CIBW_TEST_COMMAND</span><span class="p">:</span><span class="w"> </span><span class="l">pytest {project}/tests</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wheels-${{ matrix.os }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">./wheelhouse/*.whl</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">  </span><span class="nt">build_sdist</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build source distribution</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build sdist</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="sd">          pip install build
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="sd">          python -m build --sdist</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sdist</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/*.tar.gz</span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">  </span><span class="nt">publish</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">build_wheels, build_sdist]</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">pypi</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">    </span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">      </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="w">          </span><span class="nt">pattern</span><span class="p">:</span><span class="w"> </span><span class="l">wheels-*</span><span class="w">
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="w">          </span><span class="nt">merge-multiple</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sdist</span><span class="w">
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/</span><span class="w">
</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">pypa/gh-action-pypi-publish@release/v1</span></span></span></code></pre></div><h3 id="cibuildwheel-設定">cibuildwheel 設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">cibuildwheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c"># 建構的 Python 版本</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build</span> <span class="p">=</span> <span class="s2">&#34;cp39-* cp310-* cp311-* cp312-*&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c"># 跳過的組合</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">skip</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="s2">&#34;*-win32&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="s2">&#34;*-manylinux_i686&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="s2">&#34;*-musllinux*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c"># 測試設定</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nx">test-command</span> <span class="p">=</span> <span class="s2">&#34;pytest {project}/tests&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">test-requires</span> <span class="p">=</span> <span class="s2">&#34;pytest&#34;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c"># 環境變數</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">cibuildwheel</span><span class="p">.</span><span class="nx">environment</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">MY_VAR</span> <span class="p">=</span> <span class="s2">&#34;value&#34;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c"># 平台特定設定</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">cibuildwheel</span><span class="p">.</span><span class="nx">linux</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nx">archs</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;x86_64&#34;</span><span class="p">,</span> <span class="s2">&#34;aarch64&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="nx">manylinux-x86_64-image</span> <span class="p">=</span> <span class="s2">&#34;manylinux2014&#34;</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">cibuildwheel</span><span class="p">.</span><span class="nx">macos</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="nx">archs</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;x86_64&#34;</span><span class="p">,</span> <span class="s2">&#34;arm64&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">cibuildwheel</span><span class="p">.</span><span class="nx">windows</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">archs</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;AMD64&#34;</span><span class="p">]</span></span></span></code></pre></div><hr>
<h2 id="常見問題疑難排解">【常見問題】疑難排解</h2>
<h3 id="上傳失敗">上傳失敗</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">問題：HTTPError 403 Forbidden
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">原因：認證失敗
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">解決：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">1. 確認 token 正確
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">2. 確認使用 __token__ 作為使用者名稱
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">3. 確認 token 的 scope 包含該專案
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">問題：File already exists
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">原因：該版本已經上傳過
</span></span><span class="line"><span class="ln">10</span><span class="cl">解決：
</span></span><span class="line"><span class="ln">11</span><span class="cl">1. 升級版本號
</span></span><span class="line"><span class="ln">12</span><span class="cl">2. PyPI 不允許覆蓋已發布的版本
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">問題：Invalid distribution file
</span></span><span class="line"><span class="ln">15</span><span class="cl">原因：套件格式錯誤
</span></span><span class="line"><span class="ln">16</span><span class="cl">解決：
</span></span><span class="line"><span class="ln">17</span><span class="cl">1. 執行 twine check dist/*
</span></span><span class="line"><span class="ln">18</span><span class="cl">2. 確認 pyproject.toml 設定正確</span></span></code></pre></div><h3 id="安裝問題">安裝問題</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">問題：No matching distribution found
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">原因：沒有相容的 wheel
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">解決：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">1. 確認有發布 sdist
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">2. 確認有對應平台的 wheel
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">3. 檢查 requires-python 設定
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">問題：Could not build wheels
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">原因：建構失敗（通常是 C 擴展）
</span></span><span class="line"><span class="ln">10</span><span class="cl">解決：
</span></span><span class="line"><span class="ln">11</span><span class="cl">1. 安裝編譯器（gcc, MSVC）
</span></span><span class="line"><span class="ln">12</span><span class="cl">2. 安裝 python-dev 套件
</span></span><span class="line"><span class="ln">13</span><span class="cl">3. 提供預編譯的 wheel</span></span></code></pre></div><hr>
<h2 id="思考題">思考題</h2>
<ol>
<li>為什麼 PyPI 不允許刪除或覆蓋已發布的版本？</li>
<li>Trusted Publishing 相比 API token 有什麼優勢？</li>
<li>在什麼情況下應該同時發布 sdist 和 wheel？</li>
</ol>
<h2 id="實作練習">實作練習</h2>
<ol>
<li>建立一個簡單套件並發布到 TestPyPI</li>
<li>設定 GitHub Actions 自動發布流程</li>
<li>使用 cibuildwheel 建構多平台 wheel</li>
</ol>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://pypi.org/help/">PyPI 官方文件</a></li>
<li><a href="https://docs.pypi.org/trusted-publishers/">Trusted Publishing</a></li>
<li><a href="https://cibuildwheel.readthedocs.io/">cibuildwheel</a></li>
<li><a href="https://github.com/pypa/gh-action-pypi-publish">gh-action-pypi-publish</a></li>
</ul>
<hr>
<p><em>上一章：<a href="/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">建構系統比較</a></em>
<em>下一章：<a href="/blog/python-advanced/07-packaging/best-practices/" data-link-title="6.4 套件維護最佳實踐" data-link-desc="長期維護 Python 套件的最佳實踐">最佳實踐</a></em></p>
]]></content:encoded></item><item><title>6.4 套件維護最佳實踐</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/best-practices/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/best-practices/</guid><description>&lt;p>本章介紹維護 Python 套件的長期策略。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>學完本章後，你將能夠：&lt;/p>
&lt;ol>
&lt;li>建立良好的專案結構&lt;/li>
&lt;li>管理依賴與版本&lt;/li>
&lt;li>制定棄用與升級策略&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="設計層專案結構">【設計層】專案結構&lt;/h2>
&lt;h3 id="src-layout-vs-flat-layout">Src Layout vs Flat Layout&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">Flat Layout：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">my-package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── my_package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ └── module.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> └── test_module.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">Src Layout（推薦）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">my-package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">├── pyproject.toml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">│ └── my_package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">│ └── module.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> └── test_module.py&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="為什麼推薦-src-layout">為什麼推薦 Src Layout？&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">Flat Layout 的問題：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 可能意外匯入本地未安裝的套件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 測試可能使用開發版而非安裝版
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">└── 容易混淆專案根目錄和套件目錄
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">Src Layout 的優點：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">├── 強制測試已安裝的套件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── 清楚區分原始碼和設定檔
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 避免命名衝突
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">└── 更接近使用者的安裝環境&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="完整專案結構範例">完整專案結構範例&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">my-package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── .github/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">│ └── workflows/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">│ ├── ci.yml # 測試與檢查
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">│ └── publish.yml # 發布流程
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── docs/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ ├── conf.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">│ ├── index.rst
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ └── api/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│ └── my_package/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">│ ├── py.typed # 標記為有型別提示
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">│ ├── core.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">│ └── utils.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">├── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">│ ├── conftest.py # pytest fixtures
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">│ ├── test_core.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">│ └── test_utils.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">├── .gitignore
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">├── .pre-commit-config.yaml # pre-commit 設定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">├── CHANGELOG.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">├── LICENSE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">└── pyproject.toml&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層依賴管理">【實作層】依賴管理&lt;/h2>
&lt;h3 id="依賴版本約束策略">依賴版本約束策略&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">版本約束類型：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">精確版本（不推薦用於函式庫）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">└── requests==2.31.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">最小版本（推薦）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">└── requests&amp;gt;=2.28
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">相容版本（~= 運算子）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">└── requests~=2.28.0 # 等同於 &amp;gt;=2.28.0,&amp;lt;2.29.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">上限版本（謹慎使用）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">└── requests&amp;gt;=2.28,&amp;lt;3.0&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="函式庫-vs-應用程式的依賴策略">函式庫 vs 應用程式的依賴策略&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">函式庫（被其他專案依賴）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── 使用寬鬆的版本約束
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── 只指定最小版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 不鎖定間接依賴
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">└── 讓使用者決定具體版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">dependencies = [
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &amp;#34;requests&amp;gt;=2.28&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &amp;#34;click&amp;gt;=8.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">應用程式（直接部署）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">├── 使用精確的版本鎖定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">├── 鎖定所有間接依賴
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">├── 使用 lock 檔案
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">└── 確保可重現的環境
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"># 使用 Poetry/PDM 的 lock 檔案
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"># 或 pip-tools 的 requirements.txt&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="可選依賴optional-dependencies">可選依賴（Optional Dependencies）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optional-dependencies&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c"># 開發依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">dev&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest&amp;gt;=7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pytest-cov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;ruff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;mypy&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c"># 文件依賴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nx">docs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;sphinx&amp;gt;=6.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;sphinx-rtd-theme&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;myst-parser&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c"># 特定功能&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">async&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;aiohttp&amp;gt;=3.8&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="nx">cli&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;click&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;rich&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="c"># 全部安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="nx">all&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;my-package[async,cli]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="依賴更新策略">依賴更新策略&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">定期更新流程：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">1. 檢查過期依賴
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> pip list --outdated
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> # 或使用工具
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> pip-audit # 安全性檢查
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">2. 更新依賴
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> # Poetry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> poetry update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> poetry update requests # 更新特定套件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> # PDM
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> pdm update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">3. 執行測試
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> pytest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">4. 審查變更
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> git diff pyproject.toml poetry.lock
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">5. 提交更新
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> git commit -m &amp;#34;chore: update dependencies&amp;#34;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層品質保證">【實作層】品質保證&lt;/h2>
&lt;h3 id="測試策略">測試策略&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pytest&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ini_options&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">testpaths&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tests&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">python_files&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;test_*.py&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">python_functions&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;test_*&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">addopts&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;-v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;--strict-markers&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;--cov=my_package&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;--cov-report=term-missing&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;--cov-report=html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nx">markers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;slow: marks tests as slow&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;integration: marks integration tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">coverage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">run&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;src/my_package&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="nx">branch&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="nx">parallel&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">coverage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">report&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="nx">exclude_lines&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pragma: no cover&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;if TYPE_CHECKING:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;raise NotImplementedError&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;@overload&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="nx">fail_under&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">80&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="程式碼品質工具">程式碼品質工具&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># pyproject.toml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c"># Ruff（linter + formatter）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nx">line-length&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">88&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nx">target-version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;py38&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">select&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;E&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># pycodestyle errors&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;W&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># pycodestyle warnings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;F&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># pyflakes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;I&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># isort&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;UP&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># pyupgrade&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;B&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># flake8-bugbear&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;SIM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># flake8-simplify&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;RUF&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c"># Ruff-specific rules&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nx">ignore&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E501&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c"># line too long（由 formatter 處理）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ruff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isort&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="nx">known-first-party&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my_package&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="c"># Mypy（型別檢查）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mypy&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="nx">python_version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;3.8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="nx">strict&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="nx">warn_return_any&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="nx">warn_unused_ignores&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="nx">show_error_codes&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="p">[[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mypy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">overrides&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="nx">module&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tests.*&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="nx">disallow_untyped_defs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="pre-commit-設定">Pre-commit 設定&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># .pre-commit-config.yaml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">repos&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">repo&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">https://github.com/pre-commit/pre-commit-hooks&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">rev&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v4.5.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hooks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">trailing-whitespace&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">end-of-file-fixer&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">check-yaml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">check-toml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">check-added-large-files&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">repo&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">https://github.com/astral-sh/ruff-pre-commit&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">rev&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v0.3.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hooks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ruff&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>--&lt;span class="l">fix]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ruff-format&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">repo&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">https://github.com/pre-commit/mirrors-mypy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">rev&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v1.8.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hooks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">mypy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">additional_dependencies&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">types-requests]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 pre-commit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">pip install pre-commit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">pre-commit install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 手動執行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">pre-commit run --all-files&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層版本與變更管理">【實作層】版本與變更管理&lt;/h2>
&lt;h3 id="語義化版本實踐">語義化版本實踐&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">MAJOR.MINOR.PATCH
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">何時增加 MAJOR：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 移除已棄用的 API
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── 更改現有 API 的行為
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">├── 更改函式簽名（必要參數）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">└── 不再支援舊版 Python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">何時增加 MINOR：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 新增功能
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">├── 新增可選參數
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">├── 棄用現有 API（但不移除）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">└── 效能改進
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">何時增加 PATCH：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">├── 修復 bug
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">├── 安全性修補
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">├── 文件修正
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">└── 內部重構（不影響 API）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="changelog-格式">CHANGELOG 格式&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="gh"># Changelog
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="gh">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">本專案遵循 [&lt;span class="nt">Keep a Changelog&lt;/span>](&lt;span class="na">https://keepachangelog.com/&lt;/span>) 格式。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="gu">## [Unreleased]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="gu">### Added
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 新功能描述
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="gu">### Changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 變更描述
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="gu">### Deprecated
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 棄用描述
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="gu">### Removed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 移除描述
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="gu">### Fixed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 修復描述
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="gu">### Security
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 安全性修補描述
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="gu">## [1.2.0] - 2025-01-15
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="gu">### Added
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 新增 &lt;span class="sb">`process_async`&lt;/span> 函式支援非同步處理 (&lt;span class="ni">#123&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> 新增 &lt;span class="sb">`Config`&lt;/span> 類別用於設定管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="gu">### Changed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> &lt;span class="sb">`process`&lt;/span> 函式現在預設啟用快取
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="gu">### Deprecated
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> &lt;span class="sb">`old_function`&lt;/span> 將在 2.0.0 移除，請使用 &lt;span class="sb">`new_function`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">&lt;span class="gu">### Fixed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> 修復在 Windows 上的路徑處理問題 (&lt;span class="ni">#456&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="gu">## [1.1.0] - 2025-01-01
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">[Unreleased]: https://github.com/user/project/compare/v1.2.0...HEAD
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl">[1.2.0]: https://github.com/user/project/compare/v1.1.0...v1.2.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl">[1.1.0]: https://github.com/user/project/releases/tag/v1.1.0&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="自動化版本管理">自動化版本管理&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># 使用 setuptools-scm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">build-system&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nx">requires&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;setuptools&amp;gt;=61.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;setuptools-scm&amp;gt;=8.0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nx">build-backend&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;setuptools.build_meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">project&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;my-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nx">dynamic&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">tool&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setuptools_scm&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c"># 從 git tag 自動產生版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c"># v1.0.0 → 1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c"># v1.0.0-5-g1234abc → 1.0.0.dev5+g1234abc&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 發布流程&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">git tag v1.2.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">git push --tags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># CI 自動建構並發布&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層棄用策略">【實作層】棄用策略&lt;/h2>
&lt;h3 id="棄用流程">棄用流程&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">棄用時間線（範例）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">v1.0: 引入 old_function
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">v1.5: 引入 new_function
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> 標記 old_function 為棄用
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">v1.6: old_function 發出棄用警告
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">v1.7: │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">v1.8: │ 至少保留 2-3 個 minor 版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">v2.0: 移除 old_function&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="實現棄用警告">實現棄用警告&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># my_package/deprecated.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">warnings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">functools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">wraps&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Callable&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">TypeVar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="n">F&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TypeVar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;F&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bound&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">Callable&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">deprecated&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="n">reason&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="n">version&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="n">replacement&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Callable&lt;/span>&lt;span class="p">[[&lt;/span>&lt;span class="n">F&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;標記函式為棄用。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="s2"> reason: 棄用原因
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="s2"> version: 將移除的版本
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="s2"> replacement: 替代方案
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="s2"> Example:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="s2"> @deprecated(
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="s2"> reason=&amp;#34;使用新的 API&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="s2"> version=&amp;#34;2.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="s2"> replacement=&amp;#34;new_function&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="s2"> )
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="s2"> def old_function():
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="s2"> pass
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">decorator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">func&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">func&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="vm">__name__&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 已棄用，將在 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">version&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 移除。&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">reason&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">replacement&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34; 請使用 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">replacement&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 替代。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl"> &lt;span class="nd">@wraps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">func&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">wrapper&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">kwargs&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl"> &lt;span class="n">warnings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl"> &lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl"> &lt;span class="ne">DeprecationWarning&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl"> &lt;span class="n">stacklevel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">func&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">kwargs&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 更新 docstring&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl"> &lt;span class="n">wrapper&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="vm">__doc__&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;.. deprecated:: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">version&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2"> &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">func&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="vm">__doc__&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">wrapper&lt;/span> &lt;span class="c1"># type: ignore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">decorator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用範例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl">&lt;span class="nd">@deprecated&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl"> &lt;span class="n">reason&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;效能問題&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl"> &lt;span class="n">version&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;2.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl"> &lt;span class="n">replacement&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;process_v2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_v1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">58&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;處理資料（舊版）。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">59&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_v2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;處理資料（新版，更高效）。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 新的實現&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="棄用類別屬性">棄用類別屬性&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">warnings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Config&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_new_setting&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;default&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="nd">@property&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">old_setting&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;已棄用，請使用 new_setting。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="n">warnings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;old_setting 已棄用，將在 2.0.0 移除。請使用 new_setting。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="ne">DeprecationWarning&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="n">stacklevel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_new_setting&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="nd">@old_setting.setter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">old_setting&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="n">warnings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;old_setting 已棄用，將在 2.0.0 移除。請使用 new_setting。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="ne">DeprecationWarning&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="n">stacklevel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_new_setting&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="nd">@property&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">new_setting&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;新的設定屬性。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_new_setting&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl"> &lt;span class="nd">@new_setting.setter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">new_setting&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_new_setting&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">value&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層向後相容性">【實作層】向後相容性&lt;/h2>
&lt;h3 id="api-穩定性承諾">API 穩定性承諾&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">API 穩定性等級：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">Public API（穩定）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── 文件記載的函式和類別
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── __all__ 中導出的名稱
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">└── 遵循語義化版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">Internal API（不穩定）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">├── 以 _ 開頭的名稱
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">├── 未在文件中記載
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">└── 可能在任何版本變更
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">Experimental API：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">├── 明確標記為實驗性
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">├── 可能在 minor 版本變更
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">└── 不保證向後相容&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="維護向後相容的技巧">維護向後相容的技巧&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 新增可選參數時使用預設值&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_option&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="c1"># 新增 new_option&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">new_option&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 新行為&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="k">pass&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 舊行為保持不變&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 使用 **kwargs 保持彈性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">create_client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">host&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">kwargs&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 未來可以新增參數而不破壞現有程式碼&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="n">timeout&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">kwargs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;timeout&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">30&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 提供相容層&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">old_api&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">kwargs&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;已棄用，請使用 new_api。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="n">warnings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="ne">DeprecationWarning&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stacklevel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="c1"># 轉換參數格式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">new_api&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">kwargs&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="c1"># 4. 版本檢查&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">sys&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">version_info&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TypeAlias&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">typing_extensions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TypeAlias&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="實作層文件與社群">【實作層】文件與社群&lt;/h2>
&lt;h3 id="文件結構">文件結構&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">docs/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── index.rst # 首頁
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├── installation.rst # 安裝指南
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">├── quickstart.rst # 快速開始
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">├── tutorial/ # 教學
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">│ ├── basics.rst
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ └── advanced.rst
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├── api/ # API 參考
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ ├── index.rst
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">│ └── modules.rst
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">├── changelog.rst # 變更日誌
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">└── contributing.rst # 貢獻指南&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="readme-模板">README 模板&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="gh"># My Package
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="gh">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">[&lt;span class="nt">![PyPI version&lt;/span>](&lt;span class="na">https://badge.fury.io/py/my-package.svg&lt;/span>)](https://pypi.org/project/my-package/)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">[&lt;span class="nt">![Python versions&lt;/span>](&lt;span class="na">https://img.shields.io/pypi/pyversions/my-package.svg&lt;/span>)](https://pypi.org/project/my-package/)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">[&lt;span class="nt">![License&lt;/span>](&lt;span class="na">https://img.shields.io/pypi/l/my-package.svg&lt;/span>)](https://github.com/user/my-package/blob/main/LICENSE)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">[&lt;span class="nt">![CI&lt;/span>](&lt;span class="na">https://github.com/user/my-package/actions/workflows/ci.yml/badge.svg&lt;/span>)](https://github.com/user/my-package/actions)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">[&lt;span class="nt">![codecov&lt;/span>](&lt;span class="na">https://codecov.io/gh/user/my-package/branch/main/graph/badge.svg&lt;/span>)](https://codecov.io/gh/user/my-package)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">簡短描述這個套件做什麼。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="gu">## 特點
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> 功能 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> 功能 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> 功能 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="gu">## 安裝
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">\`\`\`bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">pip install my-package
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">\`\`\`
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="gu">## 快速開始
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">\`\`\`python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">from my_package import something
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">result = something.do_thing()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">\`\`\`
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="gu">## 文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">完整文件請見：https://my-package.readthedocs.io/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="gu">## 貢獻
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl">歡迎貢獻！請見 [&lt;span class="nt">CONTRIBUTING.md&lt;/span>](&lt;span class="na">/python-advanced/07-packaging/best-practices/CONTRIBUTING.md&lt;/span>)。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">&lt;span class="gu">## 授權
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">MIT License - 詳見 [&lt;span class="nt">LICENSE&lt;/span>](&lt;span class="na">/python-advanced/07-packaging/best-practices/LICENSE&lt;/span>)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="issue-與-pr-模板">Issue 與 PR 模板&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c">&amp;lt;!-- .github/ISSUE_TEMPLATE/bug_report.md --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">---
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">name: Bug 回報
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">about: 回報問題以協助改進
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">---
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="gs">**描述問題**&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">清楚簡潔地描述問題。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="gs">**重現步驟**&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="k">1.&lt;/span> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="k">2.&lt;/span> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="gs">**預期行為**&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">描述你期望的行為。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="gs">**環境**&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> OS: [e.g., macOS 14.0]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> Python: [e.g., 3.11.0]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> Package version: [e.g., 1.2.0]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="gs">**額外資訊**&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">任何其他相關資訊。&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="進階維護者工作流程">【進階】維護者工作流程&lt;/h2>
&lt;h3 id="發布檢查清單">發布檢查清單&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">發布前檢查：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">□ 所有測試通過
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">□ 程式碼覆蓋率達標
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">□ 型別檢查通過
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">□ 文件已更新
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">□ CHANGELOG 已更新
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">□ 版本號已更新
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">□ 無安全性漏洞（pip-audit）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">發布步驟：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">1. 更新 CHANGELOG
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> - 將 [Unreleased] 內容移到新版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> - 新增發布日期
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">2. 建立發布 commit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> git add CHANGELOG.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> git commit -m &amp;#34;chore: prepare release v1.2.0&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">3. 建立 tag
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> git tag v1.2.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> git push origin main --tags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">4. CI 自動發布到 PyPI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">5. 建立 GitHub Release
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> - 使用 CHANGELOG 內容
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> - 附上二進位檔案（如適用）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">6. 宣布發布
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl"> - 更新文件網站
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl"> - 社群媒體/郵件列表&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="安全性維護">安全性維護&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 檢查已知漏洞&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">pip install pip-audit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">pip-audit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 檢查依賴更新&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">pip list --outdated
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># 使用 Dependabot（GitHub）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c1"># .github/dependabot.yml&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>




&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># .github/dependabot.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">updates&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">package-ecosystem&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;pip&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">schedule&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">interval&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;weekly&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">commit-message&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">prefix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;chore(deps)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;dependencies&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">package-ecosystem&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;github-actions&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">schedule&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">interval&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;weekly&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="總結">總結&lt;/h2>
&lt;h3 id="最佳實踐清單">最佳實踐清單&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">專案結構：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">- 使用 src layout
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">- 清楚的目錄組織
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">- 包含 py.typed 標記
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">依賴管理：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">- 函式庫使用寬鬆版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">- 應用程式使用 lock 檔案
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">- 定期更新依賴
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">品質保證：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">- 完整的測試覆蓋
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">- 型別提示和 mypy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">- 使用 pre-commit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">版本管理：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">- 遵循語義化版本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">- 維護 CHANGELOG
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">- 自動化版本號
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">向後相容：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">- 清楚的棄用流程
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">- 至少 2-3 個版本的過渡期
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">- 明確的 API 穩定性承諾
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">文件與社群：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">- 完整的 README
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">- API 文件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">- 貢獻指南&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="思考題">思考題&lt;/h2>
&lt;ol>
&lt;li>函式庫和應用程式的依賴管理策略為什麼不同？各有什麼優缺點？&lt;/li>
&lt;li>如何平衡「保持向後相容」和「改進 API 設計」的矛盾？&lt;/li>
&lt;li>開源專案的維護者應該如何處理安全性漏洞的披露？&lt;/li>
&lt;/ol>
&lt;h2 id="實作練習">實作練習&lt;/h2>
&lt;ol>
&lt;li>為一個現有專案加入 pre-commit 設定，包含 ruff 和 mypy&lt;/li>
&lt;li>實作一個 &lt;code>@deprecated&lt;/code> 裝飾器，並寫測試驗證警告訊息&lt;/li>
&lt;li>為一個開源專案撰寫完整的 CONTRIBUTING.md&lt;/li>
&lt;/ol>
&lt;h2 id="延伸閱讀">延伸閱讀&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://packaging.python.org/">Python Packaging User Guide&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://keepachangelog.com/">Keep a Changelog&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://semver.org/">Semantic Versioning&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://learn.scientific-python.org/development/">Scientific Python Development Guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;em>上一章：&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/distribution/" data-link-title="6.3 發布到 PyPI" data-link-desc="學習如何建構 wheel 並發布到 PyPI">發布到 PyPI&lt;/a>&lt;/em>
&lt;em>返回：&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/" data-link-title="模組七：打包與發布" data-link-desc="學習現代 Python 套件的打包與發布流程">模組六首頁&lt;/a>&lt;/em>&lt;/p></description><content:encoded><![CDATA[<p>本章介紹維護 Python 套件的長期策略。</p>
<h2 id="本章目標">本章目標</h2>
<p>學完本章後，你將能夠：</p>
<ol>
<li>建立良好的專案結構</li>
<li>管理依賴與版本</li>
<li>制定棄用與升級策略</li>
</ol>
<hr>
<h2 id="設計層專案結構">【設計層】專案結構</h2>
<h3 id="src-layout-vs-flat-layout">Src Layout vs Flat Layout</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">Flat Layout：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">my-package/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── my_package/
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">│   ├── __init__.py
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│   └── module.py
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    └── test_module.py
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">Src Layout（推薦）：
</span></span><span class="line"><span class="ln">12</span><span class="cl">my-package/
</span></span><span class="line"><span class="ln">13</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln">14</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln">15</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln">16</span><span class="cl">│   └── my_package/
</span></span><span class="line"><span class="ln">17</span><span class="cl">│       ├── __init__.py
</span></span><span class="line"><span class="ln">18</span><span class="cl">│       └── module.py
</span></span><span class="line"><span class="ln">19</span><span class="cl">└── tests/
</span></span><span class="line"><span class="ln">20</span><span class="cl">    └── test_module.py</span></span></code></pre></div><h3 id="為什麼推薦-src-layout">為什麼推薦 Src Layout？</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">Flat Layout 的問題：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 可能意外匯入本地未安裝的套件
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 測試可能使用開發版而非安裝版
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">└── 容易混淆專案根目錄和套件目錄
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Src Layout 的優點：
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">├── 強制測試已安裝的套件
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 清楚區分原始碼和設定檔
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 避免命名衝突
</span></span><span class="line"><span class="ln">10</span><span class="cl">└── 更接近使用者的安裝環境</span></span></code></pre></div><h3 id="完整專案結構範例">完整專案結構範例</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">my-package/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── .github/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">│   └── workflows/
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">│       ├── ci.yml              # 測試與檢查
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│       └── publish.yml         # 發布流程
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── docs/
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│   ├── conf.py
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">│   ├── index.rst
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│   └── api/
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln">11</span><span class="cl">│   └── my_package/
</span></span><span class="line"><span class="ln">12</span><span class="cl">│       ├── __init__.py
</span></span><span class="line"><span class="ln">13</span><span class="cl">│       ├── py.typed            # 標記為有型別提示
</span></span><span class="line"><span class="ln">14</span><span class="cl">│       ├── core.py
</span></span><span class="line"><span class="ln">15</span><span class="cl">│       └── utils.py
</span></span><span class="line"><span class="ln">16</span><span class="cl">├── tests/
</span></span><span class="line"><span class="ln">17</span><span class="cl">│   ├── __init__.py
</span></span><span class="line"><span class="ln">18</span><span class="cl">│   ├── conftest.py             # pytest fixtures
</span></span><span class="line"><span class="ln">19</span><span class="cl">│   ├── test_core.py
</span></span><span class="line"><span class="ln">20</span><span class="cl">│   └── test_utils.py
</span></span><span class="line"><span class="ln">21</span><span class="cl">├── .gitignore
</span></span><span class="line"><span class="ln">22</span><span class="cl">├── .pre-commit-config.yaml     # pre-commit 設定
</span></span><span class="line"><span class="ln">23</span><span class="cl">├── CHANGELOG.md
</span></span><span class="line"><span class="ln">24</span><span class="cl">├── LICENSE
</span></span><span class="line"><span class="ln">25</span><span class="cl">├── README.md
</span></span><span class="line"><span class="ln">26</span><span class="cl">└── pyproject.toml</span></span></code></pre></div><hr>
<h2 id="實作層依賴管理">【實作層】依賴管理</h2>
<h3 id="依賴版本約束策略">依賴版本約束策略</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">版本約束類型：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">精確版本（不推薦用於函式庫）：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">└── requests==2.31.0
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">最小版本（推薦）：
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">└── requests&gt;=2.28
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">相容版本（~= 運算子）：
</span></span><span class="line"><span class="ln">10</span><span class="cl">└── requests~=2.28.0  # 等同於 &gt;=2.28.0,&lt;2.29.0
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">上限版本（謹慎使用）：
</span></span><span class="line"><span class="ln">13</span><span class="cl">└── requests&gt;=2.28,&lt;3.0</span></span></code></pre></div><h3 id="函式庫-vs-應用程式的依賴策略">函式庫 vs 應用程式的依賴策略</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">函式庫（被其他專案依賴）：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 使用寬鬆的版本約束
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 只指定最小版本
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 不鎖定間接依賴
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── 讓使用者決定具體版本
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">dependencies = [
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    &#34;requests&gt;=2.28&#34;,
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    &#34;click&gt;=8.0&#34;,
</span></span><span class="line"><span class="ln">10</span><span class="cl">]
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">應用程式（直接部署）：
</span></span><span class="line"><span class="ln">13</span><span class="cl">├── 使用精確的版本鎖定
</span></span><span class="line"><span class="ln">14</span><span class="cl">├── 鎖定所有間接依賴
</span></span><span class="line"><span class="ln">15</span><span class="cl">├── 使用 lock 檔案
</span></span><span class="line"><span class="ln">16</span><span class="cl">└── 確保可重現的環境
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"># 使用 Poetry/PDM 的 lock 檔案
</span></span><span class="line"><span class="ln">19</span><span class="cl"># 或 pip-tools 的 requirements.txt</span></span></code></pre></div><h3 id="可選依賴optional-dependencies">可選依賴（Optional Dependencies）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">.</span><span class="nx">optional-dependencies</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c"># 開發依賴</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">dev</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="s2">&#34;pytest&gt;=7.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="s2">&#34;pytest-cov&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="s2">&#34;ruff&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="s2">&#34;mypy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c"># 文件依賴</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">docs</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="s2">&#34;sphinx&gt;=6.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;sphinx-rtd-theme&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s2">&#34;myst-parser&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c"># 特定功能</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">async</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;aiohttp&gt;=3.8&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nx">cli</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;click&gt;=8.0&#34;</span><span class="p">,</span> <span class="s2">&#34;rich&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c"># 全部安裝</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nx">all</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">&#34;my-package[async,cli]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">]</span></span></span></code></pre></div><h3 id="依賴更新策略">依賴更新策略</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">定期更新流程：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">1. 檢查過期依賴
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">   pip list --outdated
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   # 或使用工具
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">   pip-audit  # 安全性檢查
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">2. 更新依賴
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   # Poetry
</span></span><span class="line"><span class="ln">10</span><span class="cl">   poetry update
</span></span><span class="line"><span class="ln">11</span><span class="cl">   poetry update requests  # 更新特定套件
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">   # PDM
</span></span><span class="line"><span class="ln">14</span><span class="cl">   pdm update
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">3. 執行測試
</span></span><span class="line"><span class="ln">17</span><span class="cl">   pytest
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">4. 審查變更
</span></span><span class="line"><span class="ln">20</span><span class="cl">   git diff pyproject.toml poetry.lock
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">5. 提交更新
</span></span><span class="line"><span class="ln">23</span><span class="cl">   git commit -m &#34;chore: update dependencies&#34;</span></span></code></pre></div><hr>
<h2 id="實作層品質保證">【實作層】品質保證</h2>
<h3 id="測試策略">測試策略</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">pytest</span><span class="p">.</span><span class="nx">ini_options</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">testpaths</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;tests&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">python_files</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;test_*.py&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">python_functions</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;test_*&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">addopts</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="s2">&#34;-v&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="s2">&#34;--strict-markers&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="s2">&#34;--cov=my_package&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="s2">&#34;--cov-report=term-missing&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s2">&#34;--cov-report=html&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">markers</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;slow: marks tests as slow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s2">&#34;integration: marks integration tests&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">coverage</span><span class="p">.</span><span class="nx">run</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">source</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;src/my_package&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nx">branch</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nx">parallel</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">coverage</span><span class="p">.</span><span class="nx">report</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="nx">exclude_lines</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="s2">&#34;pragma: no cover&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="s2">&#34;if TYPE_CHECKING:&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="s2">&#34;raise NotImplementedError&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="s2">&#34;@overload&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">fail_under</span> <span class="p">=</span> <span class="mi">80</span></span></span></code></pre></div><h3 id="程式碼品質工具">程式碼品質工具</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># pyproject.toml</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c"># Ruff（linter + formatter）</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">line-length</span> <span class="p">=</span> <span class="mi">88</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">target-version</span> <span class="p">=</span> <span class="s2">&#34;py38&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">select</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="s2">&#34;E&#34;</span><span class="p">,</span>   <span class="c"># pycodestyle errors</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s2">&#34;W&#34;</span><span class="p">,</span>   <span class="c"># pycodestyle warnings</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="s2">&#34;F&#34;</span><span class="p">,</span>   <span class="c"># pyflakes</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="s2">&#34;I&#34;</span><span class="p">,</span>   <span class="c"># isort</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;UP&#34;</span><span class="p">,</span>  <span class="c"># pyupgrade</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s2">&#34;B&#34;</span><span class="p">,</span>   <span class="c"># flake8-bugbear</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="s2">&#34;SIM&#34;</span><span class="p">,</span> <span class="c"># flake8-simplify</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="s2">&#34;RUF&#34;</span><span class="p">,</span> <span class="c"># Ruff-specific rules</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nx">ignore</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;E501&#34;</span><span class="p">]</span>  <span class="c"># line too long（由 formatter 處理）</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">ruff</span><span class="p">.</span><span class="nx">lint</span><span class="p">.</span><span class="nx">isort</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">known-first-party</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;my_package&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c"># Mypy（型別檢查）</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">mypy</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="nx">python_version</span> <span class="p">=</span> <span class="s2">&#34;3.8&#34;</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="nx">strict</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="nx">warn_return_any</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="nx">warn_unused_ignores</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="nx">show_error_codes</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">[[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">mypy</span><span class="p">.</span><span class="nx">overrides</span><span class="p">]]</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="nx">module</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;tests.*&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="nx">disallow_untyped_defs</span> <span class="p">=</span> <span class="kc">false</span></span></span></code></pre></div><h3 id="pre-commit-設定">Pre-commit 設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .pre-commit-config.yaml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">repos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span>- <span class="nt">repo</span><span class="p">:</span><span class="w"> </span><span class="l">https://github.com/pre-commit/pre-commit-hooks</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="nt">rev</span><span class="p">:</span><span class="w"> </span><span class="l">v4.5.0</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">hooks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">trailing-whitespace</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">end-of-file-fixer</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">check-yaml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">check-toml</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">check-added-large-files</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span>- <span class="nt">repo</span><span class="p">:</span><span class="w"> </span><span class="l">https://github.com/astral-sh/ruff-pre-commit</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">rev</span><span class="p">:</span><span class="w"> </span><span class="l">v0.3.0</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">hooks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">ruff</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>--<span class="l">fix]</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">ruff-format</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">  </span>- <span class="nt">repo</span><span class="p">:</span><span class="w"> </span><span class="l">https://github.com/pre-commit/mirrors-mypy</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="nt">rev</span><span class="p">:</span><span class="w"> </span><span class="l">v1.8.0</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="nt">hooks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">mypy</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nt">additional_dependencies</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">types-requests]</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 安裝 pre-commit</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">pip install pre-commit
</span></span><span class="line"><span class="ln">3</span><span class="cl">pre-commit install
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 手動執行</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">pre-commit run --all-files</span></span></code></pre></div><hr>
<h2 id="實作層版本與變更管理">【實作層】版本與變更管理</h2>
<h3 id="語義化版本實踐">語義化版本實踐</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">MAJOR.MINOR.PATCH
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">何時增加 MAJOR：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 移除已棄用的 API
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── 更改現有 API 的行為
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├── 更改函式簽名（必要參數）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">└── 不再支援舊版 Python
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">何時增加 MINOR：
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 新增功能
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── 新增可選參數
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── 棄用現有 API（但不移除）
</span></span><span class="line"><span class="ln">13</span><span class="cl">└── 效能改進
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">何時增加 PATCH：
</span></span><span class="line"><span class="ln">16</span><span class="cl">├── 修復 bug
</span></span><span class="line"><span class="ln">17</span><span class="cl">├── 安全性修補
</span></span><span class="line"><span class="ln">18</span><span class="cl">├── 文件修正
</span></span><span class="line"><span class="ln">19</span><span class="cl">└── 內部重構（不影響 API）</span></span></code></pre></div><h3 id="changelog-格式">CHANGELOG 格式</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># Changelog
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">本專案遵循 [<span class="nt">Keep a Changelog</span>](<span class="na">https://keepachangelog.com/</span>) 格式。
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">## [Unreleased]
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### Added
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span><span class="k">-</span> 新功能描述
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### Changed
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span><span class="k">-</span> 變更描述
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### Deprecated
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">-</span> 棄用描述
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### Removed
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span><span class="k">-</span> 移除描述
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### Fixed
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="k">-</span> 修復描述
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### Security
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> 安全性修補描述
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu">## [1.2.0] - 2025-01-15
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">### Added
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">-</span> 新增 <span class="sb">`process_async`</span> 函式支援非同步處理 (<span class="ni">#123</span>)
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 新增 <span class="sb">`Config`</span> 類別用於設定管理
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu">### Changed
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu"></span><span class="k">-</span> <span class="sb">`process`</span> 函式現在預設啟用快取
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gu">### Deprecated
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu"></span><span class="k">-</span> <span class="sb">`old_function`</span> 將在 2.0.0 移除，請使用 <span class="sb">`new_function`</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="gu">### Fixed
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="gu"></span><span class="k">-</span> 修復在 Windows 上的路徑處理問題 (<span class="ni">#456</span>)
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu">## [1.1.0] - 2025-01-01
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="gu"></span>...
</span></span><span class="line"><span class="ln">42</span><span class="cl">
</span></span><span class="line"><span class="ln">43</span><span class="cl">[Unreleased]: https://github.com/user/project/compare/v1.2.0...HEAD
</span></span><span class="line"><span class="ln">44</span><span class="cl">[1.2.0]: https://github.com/user/project/compare/v1.1.0...v1.2.0
</span></span><span class="line"><span class="ln">45</span><span class="cl">[1.1.0]: https://github.com/user/project/releases/tag/v1.1.0</span></span></code></pre></div><h3 id="自動化版本管理">自動化版本管理</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 使用 setuptools-scm</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;setuptools&gt;=61.0&#34;</span><span class="p">,</span> <span class="s2">&#34;setuptools-scm&gt;=8.0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;setuptools.build_meta&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">dynamic</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;version&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">setuptools_scm</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c"># 從 git tag 自動產生版本</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c"># v1.0.0 → 1.0.0</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c"># v1.0.0-5-g1234abc → 1.0.0.dev5+g1234abc</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 發布流程</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">git tag v1.2.0
</span></span><span class="line"><span class="ln">3</span><span class="cl">git push --tags
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># CI 自動建構並發布</span></span></span></code></pre></div><hr>
<h2 id="實作層棄用策略">【實作層】棄用策略</h2>
<h3 id="棄用流程">棄用流程</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">棄用時間線（範例）：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">v1.0: 引入 old_function
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">      │
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">v1.5: 引入 new_function
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      標記 old_function 為棄用
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      │
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">v1.6: old_function 發出棄用警告
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">v1.7: │
</span></span><span class="line"><span class="ln">10</span><span class="cl">v1.8: │ 至少保留 2-3 個 minor 版本
</span></span><span class="line"><span class="ln">11</span><span class="cl">      │
</span></span><span class="line"><span class="ln">12</span><span class="cl">v2.0: 移除 old_function</span></span></code></pre></div><h3 id="實現棄用警告">實現棄用警告</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># my_package/deprecated.py</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">warnings</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">TypeVar</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">F</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&#34;F&#34;</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="n">Callable</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">def</span> <span class="nf">deprecated</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">reason</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">version</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">replacement</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">F</span><span class="p">],</span> <span class="n">F</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="s2">&#34;&#34;&#34;標記函式為棄用。
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s2">        reason: 棄用原因
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="s2">        version: 將移除的版本
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="s2">        replacement: 替代方案
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="s2">    Example:
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="s2">        @deprecated(
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="s2">            reason=&#34;使用新的 API&#34;,
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="s2">            version=&#34;2.0.0&#34;,
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="s2">            replacement=&#34;new_function&#34;,
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="s2">        )
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="s2">        def old_function():
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="s2">            pass
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">F</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> 已棄用，將在 </span><span class="si">{</span><span class="n">version</span><span class="si">}</span><span class="s2"> 移除。</span><span class="si">{</span><span class="n">reason</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="k">if</span> <span class="n">replacement</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">            <span class="n">message</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&#34; 請使用 </span><span class="si">{</span><span class="n">replacement</span><span class="si">}</span><span class="s2"> 替代。&#34;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl">        <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">            <span class="n">warnings</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">                <span class="n">message</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">                <span class="ne">DeprecationWarning</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">                <span class="n">stacklevel</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">            <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl">        <span class="c1"># 更新 docstring</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">        <span class="n">wrapper</span><span class="o">.</span><span class="vm">__doc__</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;.. deprecated:: </span><span class="si">{</span><span class="n">version</span><span class="si">}</span><span class="se">\n</span><span class="s2">   </span><span class="si">{</span><span class="n">message</span><span class="si">}</span><span class="se">\n\n</span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__doc__</span> <span class="ow">or</span> <span class="s1">&#39;&#39;</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">
</span></span><span class="line"><span class="ln">47</span><span class="cl">        <span class="k">return</span> <span class="n">wrapper</span>  <span class="c1"># type: ignore</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="k">return</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1"># 使用範例</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="nd">@deprecated</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">    <span class="n">reason</span><span class="o">=</span><span class="s2">&#34;效能問題&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">    <span class="n">replacement</span><span class="o">=</span><span class="s2">&#34;process_v2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="k">def</span> <span class="nf">process_v1</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">list</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">    <span class="s2">&#34;&#34;&#34;處理資料（舊版）。&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">x</span> <span class="o">*</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">data</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="k">def</span> <span class="nf">process_v2</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">list</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">    <span class="s2">&#34;&#34;&#34;處理資料（新版，更高效）。&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">    <span class="c1"># 新的實現</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">x</span> <span class="o">*</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">data</span><span class="p">]</span></span></span></code></pre></div><h3 id="棄用類別屬性">棄用類別屬性</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">warnings</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_new_setting</span> <span class="o">=</span> <span class="s2">&#34;default&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="k">def</span> <span class="nf">old_setting</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="s2">&#34;&#34;&#34;已棄用，請使用 new_setting。&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="n">warnings</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">            <span class="s2">&#34;old_setting 已棄用，將在 2.0.0 移除。請使用 new_setting。&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">            <span class="ne">DeprecationWarning</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">            <span class="n">stacklevel</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_new_setting</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nd">@old_setting.setter</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="k">def</span> <span class="nf">old_setting</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="n">warnings</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">            <span class="s2">&#34;old_setting 已棄用，將在 2.0.0 移除。請使用 new_setting。&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">            <span class="ne">DeprecationWarning</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">            <span class="n">stacklevel</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_new_setting</span> <span class="o">=</span> <span class="n">value</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="k">def</span> <span class="nf">new_setting</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="s2">&#34;&#34;&#34;新的設定屬性。&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_new_setting</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="nd">@new_setting.setter</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="k">def</span> <span class="nf">new_setting</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_new_setting</span> <span class="o">=</span> <span class="n">value</span></span></span></code></pre></div><hr>
<h2 id="實作層向後相容性">【實作層】向後相容性</h2>
<h3 id="api-穩定性承諾">API 穩定性承諾</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">API 穩定性等級：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">Public API（穩定）：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 文件記載的函式和類別
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── __all__ 中導出的名稱
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">└── 遵循語義化版本
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">Internal API（不穩定）：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 以 _ 開頭的名稱
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 未在文件中記載
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── 可能在任何版本變更
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Experimental API：
</span></span><span class="line"><span class="ln">14</span><span class="cl">├── 明確標記為實驗性
</span></span><span class="line"><span class="ln">15</span><span class="cl">├── 可能在 minor 版本變更
</span></span><span class="line"><span class="ln">16</span><span class="cl">└── 不保證向後相容</span></span></code></pre></div><h3 id="維護向後相容的技巧">維護向後相容的技巧</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. 新增可選參數時使用預設值</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">new_option</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>  <span class="c1"># 新增 new_option</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="k">if</span> <span class="n">new_option</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="c1"># 新行為</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="c1"># 舊行為保持不變</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># 2. 使用 **kwargs 保持彈性</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">def</span> <span class="nf">create_client</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="c1"># 未來可以新增參數而不破壞現有程式碼</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">timeout</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;timeout&#34;</span><span class="p">,</span> <span class="mi">30</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"># 3. 提供相容層</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">def</span> <span class="nf">old_api</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="s2">&#34;&#34;&#34;已棄用，請使用 new_api。&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">warnings</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="s2">&#34;...&#34;</span><span class="p">,</span> <span class="ne">DeprecationWarning</span><span class="p">,</span> <span class="n">stacklevel</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="c1"># 轉換參數格式</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">return</span> <span class="n">new_api</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"># 4. 版本檢查</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">version_info</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypeAlias</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="kn">from</span> <span class="nn">typing_extensions</span> <span class="kn">import</span> <span class="n">TypeAlias</span></span></span></code></pre></div><hr>
<h2 id="實作層文件與社群">【實作層】文件與社群</h2>
<h3 id="文件結構">文件結構</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">docs/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── index.rst           # 首頁
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── installation.rst    # 安裝指南
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── quickstart.rst      # 快速開始
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── tutorial/           # 教學
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">│   ├── basics.rst
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│   └── advanced.rst
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── api/                # API 參考
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│   ├── index.rst
</span></span><span class="line"><span class="ln">10</span><span class="cl">│   └── modules.rst
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── changelog.rst       # 變更日誌
</span></span><span class="line"><span class="ln">12</span><span class="cl">└── contributing.rst    # 貢獻指南</span></span></code></pre></div><h3 id="readme-模板">README 模板</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># My Package
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">[<span class="nt">![PyPI version</span>](<span class="na">https://badge.fury.io/py/my-package.svg</span>)](https://pypi.org/project/my-package/)
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">[<span class="nt">![Python versions</span>](<span class="na">https://img.shields.io/pypi/pyversions/my-package.svg</span>)](https://pypi.org/project/my-package/)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">[<span class="nt">![License</span>](<span class="na">https://img.shields.io/pypi/l/my-package.svg</span>)](https://github.com/user/my-package/blob/main/LICENSE)
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">[<span class="nt">![CI</span>](<span class="na">https://github.com/user/my-package/actions/workflows/ci.yml/badge.svg</span>)](https://github.com/user/my-package/actions)
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">[<span class="nt">![codecov</span>](<span class="na">https://codecov.io/gh/user/my-package/branch/main/graph/badge.svg</span>)](https://codecov.io/gh/user/my-package)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">簡短描述這個套件做什麼。
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">## 特點
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 功能 1
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> 功能 2
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> 功能 3
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu">## 安裝
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">\`\`\`bash
</span></span><span class="line"><span class="ln">20</span><span class="cl">pip install my-package
</span></span><span class="line"><span class="ln">21</span><span class="cl">\`\`\`
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">## 快速開始
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">\`\`\`python
</span></span><span class="line"><span class="ln">26</span><span class="cl">from my_package import something
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">result = something.do_thing()
</span></span><span class="line"><span class="ln">29</span><span class="cl">\`\`\`
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu">## 文件
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">完整文件請見：https://my-package.readthedocs.io/
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu">## 貢獻
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">歡迎貢獻！請見 [<span class="nt">CONTRIBUTING.md</span>](<span class="na">/python-advanced/07-packaging/best-practices/CONTRIBUTING.md</span>)。
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="gu">## 授權
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">MIT License - 詳見 [<span class="nt">LICENSE</span>](<span class="na">/python-advanced/07-packaging/best-practices/LICENSE</span>)</span></span></code></pre></div><h3 id="issue-與-pr-模板">Issue 與 PR 模板</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c">&lt;!-- .github/ISSUE_TEMPLATE/bug_report.md --&gt;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">name: Bug 回報
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">about: 回報問題以協助改進
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gs">**描述問題**</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">清楚簡潔地描述問題。
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**重現步驟**</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">1.</span> ...
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">2.</span> ...
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gs">**預期行為**</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">描述你期望的行為。
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gs">**環境**</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> OS: [e.g., macOS 14.0]
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">-</span> Python: [e.g., 3.11.0]
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> Package version: [e.g., 1.2.0]
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gs">**額外資訊**</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">任何其他相關資訊。</span></span></code></pre></div><hr>
<h2 id="進階維護者工作流程">【進階】維護者工作流程</h2>
<h3 id="發布檢查清單">發布檢查清單</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">發布前檢查：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">□ 所有測試通過
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">□ 程式碼覆蓋率達標
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">□ 型別檢查通過
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">□ 文件已更新
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">□ CHANGELOG 已更新
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">□ 版本號已更新
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">□ 無安全性漏洞（pip-audit）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">發布步驟：
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">1. 更新 CHANGELOG
</span></span><span class="line"><span class="ln">14</span><span class="cl">   - 將 [Unreleased] 內容移到新版本
</span></span><span class="line"><span class="ln">15</span><span class="cl">   - 新增發布日期
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">2. 建立發布 commit
</span></span><span class="line"><span class="ln">18</span><span class="cl">   git add CHANGELOG.md
</span></span><span class="line"><span class="ln">19</span><span class="cl">   git commit -m &#34;chore: prepare release v1.2.0&#34;
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">3. 建立 tag
</span></span><span class="line"><span class="ln">22</span><span class="cl">   git tag v1.2.0
</span></span><span class="line"><span class="ln">23</span><span class="cl">   git push origin main --tags
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">4. CI 自動發布到 PyPI
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">5. 建立 GitHub Release
</span></span><span class="line"><span class="ln">28</span><span class="cl">   - 使用 CHANGELOG 內容
</span></span><span class="line"><span class="ln">29</span><span class="cl">   - 附上二進位檔案（如適用）
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">6. 宣布發布
</span></span><span class="line"><span class="ln">32</span><span class="cl">   - 更新文件網站
</span></span><span class="line"><span class="ln">33</span><span class="cl">   - 社群媒體/郵件列表</span></span></code></pre></div><h3 id="安全性維護">安全性維護</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 檢查已知漏洞</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">pip install pip-audit
</span></span><span class="line"><span class="ln">3</span><span class="cl">pip-audit
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 檢查依賴更新</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">pip list --outdated
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># 使用 Dependabot（GitHub）</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"># .github/dependabot.yml</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/dependabot.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">updates</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span>- <span class="nt">package-ecosystem</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;pip&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">directory</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">schedule</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">      </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;weekly&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">commit-message</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">      </span><span class="nt">prefix</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;chore(deps)&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">      </span>- <span class="s2">&#34;dependencies&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">  </span>- <span class="nt">package-ecosystem</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;github-actions&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">directory</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="nt">schedule</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">      </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;weekly&#34;</span></span></span></code></pre></div><hr>
<h2 id="總結">總結</h2>
<h3 id="最佳實踐清單">最佳實踐清單</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">專案結構：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 使用 src layout
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 清楚的目錄組織
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 包含 py.typed 標記
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">依賴管理：
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">- 函式庫使用寬鬆版本
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 應用程式使用 lock 檔案
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 定期更新依賴
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">品質保證：
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 完整的測試覆蓋
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 型別提示和 mypy
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 使用 pre-commit
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">版本管理：
</span></span><span class="line"><span class="ln">17</span><span class="cl">- 遵循語義化版本
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 維護 CHANGELOG
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 自動化版本號
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">向後相容：
</span></span><span class="line"><span class="ln">22</span><span class="cl">- 清楚的棄用流程
</span></span><span class="line"><span class="ln">23</span><span class="cl">- 至少 2-3 個版本的過渡期
</span></span><span class="line"><span class="ln">24</span><span class="cl">- 明確的 API 穩定性承諾
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">文件與社群：
</span></span><span class="line"><span class="ln">27</span><span class="cl">- 完整的 README
</span></span><span class="line"><span class="ln">28</span><span class="cl">- API 文件
</span></span><span class="line"><span class="ln">29</span><span class="cl">- 貢獻指南</span></span></code></pre></div><hr>
<h2 id="思考題">思考題</h2>
<ol>
<li>函式庫和應用程式的依賴管理策略為什麼不同？各有什麼優缺點？</li>
<li>如何平衡「保持向後相容」和「改進 API 設計」的矛盾？</li>
<li>開源專案的維護者應該如何處理安全性漏洞的披露？</li>
</ol>
<h2 id="實作練習">實作練習</h2>
<ol>
<li>為一個現有專案加入 pre-commit 設定，包含 ruff 和 mypy</li>
<li>實作一個 <code>@deprecated</code> 裝飾器，並寫測試驗證警告訊息</li>
<li>為一個開源專案撰寫完整的 CONTRIBUTING.md</li>
</ol>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://packaging.python.org/">Python Packaging User Guide</a></li>
<li><a href="https://keepachangelog.com/">Keep a Changelog</a></li>
<li><a href="https://semver.org/">Semantic Versioning</a></li>
<li><a href="https://learn.scientific-python.org/development/">Scientific Python Development Guide</a></li>
</ul>
<hr>
<p><em>上一章：<a href="/blog/python-advanced/07-packaging/distribution/" data-link-title="6.3 發布到 PyPI" data-link-desc="學習如何建構 wheel 並發布到 PyPI">發布到 PyPI</a></em>
<em>返回：<a href="/blog/python-advanced/07-packaging/" data-link-title="模組七：打包與發布" data-link-desc="學習現代 Python 套件的打包與發布流程">模組六首頁</a></em></p>
]]></content:encoded></item><item><title>6.5 封裝預編譯二進位</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/bundled-binaries/</link><pubDate>Mon, 02 Feb 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/bundled-binaries/</guid><description>&lt;p>本章介紹 Python 套件封裝預編譯二進位的架構模式，讓 Python 能夠調用高效能的原生程式碼。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>學完本章後，你將能夠：&lt;/p>
&lt;ol>
&lt;li>理解「Python 封裝二進位」的架構模式&lt;/li>
&lt;li>評估何時使用這種模式&lt;/li>
&lt;li>了解知名套件如何應用這種技術&lt;/li>
&lt;li>在純 Python 與封裝二進位之間做出正確選擇&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="概念什麼是封裝預編譯二進位">【概念】什麼是封裝預編譯二進位？&lt;/h2>
&lt;h3 id="架構模式">架構模式&lt;/h3>
&lt;p>這種模式將其他語言（如 Go、Rust、C++）編譯的二進位檔案，包裝在 Python 套件中：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">┌─────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">│ Python API（薄封裝層） │ ← 使用者接觸的介面
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├─────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">│ subprocess / FFI / ctypes / cffi │ ← 呼叫機制
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">├─────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">│ 預編譯二進位（Go/Rust/C/C++） │ ← 實際執行邏輯
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">└─────────────────────────────────────────┘&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="與-c-擴展的差異">與 C 擴展的差異&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>C 擴展（模組四/五）&lt;/th>
 &lt;th>封裝預編譯二進位&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>編譯時機&lt;/td>
 &lt;td>安裝時編譯&lt;/td>
 &lt;td>發布前預編譯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>使用者需求&lt;/td>
 &lt;td>可能需要編譯器&lt;/td>
 &lt;td>不需要編譯器&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>整合方式&lt;/td>
 &lt;td>Python C API / pybind11&lt;/td>
 &lt;td>subprocess / FFI&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>典型來源&lt;/td>
 &lt;td>專為 Python 寫的擴展&lt;/td>
 &lt;td>獨立的 CLI 工具或函式庫&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="案例">【案例】&lt;/h2>
&lt;h3 id="tensorflow--pytorch">TensorFlow / PyTorch&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">TensorFlow 架構：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── Python API（tf.* 模組）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">│ └── 使用者撰寫的程式碼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── 綁定層（pybind11）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">│ └── Python ↔ C++ 橋接
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">└── C++ 核心 + CUDA
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl"> └── 預編譯的運算核心&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>選擇原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>GPU 運算需要原生效能&lt;/li>
&lt;li>大量的 C++ 程式碼庫&lt;/li>
&lt;li>安裝時編譯太慢（數小時）&lt;/li>
&lt;/ul>
&lt;h3 id="cryptography">cryptography&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">cryptography 架構：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── Python API（cryptography.*)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">├── cffi 綁定層
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">└── OpenSSL / BoringSSL
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> └── 預編譯的加密函式庫&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>選擇原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>加密演算法需要經過審計的實現&lt;/li>
&lt;li>效能關鍵&lt;/li>
&lt;li>支援 PyPy（cffi 是 PyPy 官方推薦）&lt;/li>
&lt;/ul>
&lt;h3 id="ruffpython-linter">ruff（Python Linter）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ruff 架構：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── Python 封裝（ruff 套件）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">│ └── 提供 CLI 和簡單 API
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">└── Rust 二進位
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> └── 實際的 lint 邏輯&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>選擇原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>追求極致速度（比 Flake8 快 10-100 倍）&lt;/li>
&lt;li>Rust 的記憶體安全&lt;/li>
&lt;li>作為獨立工具也可使用&lt;/li>
&lt;/ul>
&lt;h3 id="mermaid-asciipypi-封裝">mermaid-ascii（PyPI 封裝）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">osl-packages/mermaid-ascii 架構：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── Python API（mermaid_ascii 模組）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">│ └── mermaid_to_ascii() 函式
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── subprocess 呼叫
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">└── Go 編譯的 mermaid-ascii 二進位
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> └── Mermaid → ASCII 轉換&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>選擇原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>重用現有的 Go 實現&lt;/li>
&lt;li>不需要 Node.js 依賴&lt;/li>
&lt;li>提供 Python 友善的介面&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="優點為什麼使用這種模式">【優點】為什麼使用這種模式？&lt;/h2>
&lt;h3 id="1-效能">1. 效能&lt;/h3>
&lt;p>核心邏輯用高效能語言實現，Python 只做介面：&lt;/p></description><content:encoded><![CDATA[<p>本章介紹 Python 套件封裝預編譯二進位的架構模式，讓 Python 能夠調用高效能的原生程式碼。</p>
<h2 id="本章目標">本章目標</h2>
<p>學完本章後，你將能夠：</p>
<ol>
<li>理解「Python 封裝二進位」的架構模式</li>
<li>評估何時使用這種模式</li>
<li>了解知名套件如何應用這種技術</li>
<li>在純 Python 與封裝二進位之間做出正確選擇</li>
</ol>
<hr>
<h2 id="概念什麼是封裝預編譯二進位">【概念】什麼是封裝預編譯二進位？</h2>
<h3 id="架構模式">架構模式</h3>
<p>這種模式將其他語言（如 Go、Rust、C++）編譯的二進位檔案，包裝在 Python 套件中：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">┌─────────────────────────────────────────┐
</span></span><span class="line"><span class="ln">2</span><span class="cl">│       Python API（薄封裝層）              │  ← 使用者接觸的介面
</span></span><span class="line"><span class="ln">3</span><span class="cl">├─────────────────────────────────────────┤
</span></span><span class="line"><span class="ln">4</span><span class="cl">│   subprocess / FFI / ctypes / cffi      │  ← 呼叫機制
</span></span><span class="line"><span class="ln">5</span><span class="cl">├─────────────────────────────────────────┤
</span></span><span class="line"><span class="ln">6</span><span class="cl">│   預編譯二進位（Go/Rust/C/C++）          │  ← 實際執行邏輯
</span></span><span class="line"><span class="ln">7</span><span class="cl">└─────────────────────────────────────────┘</span></span></code></pre></div><h3 id="與-c-擴展的差異">與 C 擴展的差異</h3>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>C 擴展（模組四/五）</th>
          <th>封裝預編譯二進位</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>編譯時機</td>
          <td>安裝時編譯</td>
          <td>發布前預編譯</td>
      </tr>
      <tr>
          <td>使用者需求</td>
          <td>可能需要編譯器</td>
          <td>不需要編譯器</td>
      </tr>
      <tr>
          <td>整合方式</td>
          <td>Python C API / pybind11</td>
          <td>subprocess / FFI</td>
      </tr>
      <tr>
          <td>典型來源</td>
          <td>專為 Python 寫的擴展</td>
          <td>獨立的 CLI 工具或函式庫</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="案例">【案例】</h2>
<h3 id="tensorflow--pytorch">TensorFlow / PyTorch</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">TensorFlow 架構：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── Python API（tf.* 模組）
</span></span><span class="line"><span class="ln">3</span><span class="cl">│   └── 使用者撰寫的程式碼
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── 綁定層（pybind11）
</span></span><span class="line"><span class="ln">5</span><span class="cl">│   └── Python ↔ C++ 橋接
</span></span><span class="line"><span class="ln">6</span><span class="cl">└── C++ 核心 + CUDA
</span></span><span class="line"><span class="ln">7</span><span class="cl">    └── 預編譯的運算核心</span></span></code></pre></div><p><strong>選擇原因</strong>：</p>
<ul>
<li>GPU 運算需要原生效能</li>
<li>大量的 C++ 程式碼庫</li>
<li>安裝時編譯太慢（數小時）</li>
</ul>
<h3 id="cryptography">cryptography</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">cryptography 架構：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── Python API（cryptography.*)
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── cffi 綁定層
</span></span><span class="line"><span class="ln">4</span><span class="cl">└── OpenSSL / BoringSSL
</span></span><span class="line"><span class="ln">5</span><span class="cl">    └── 預編譯的加密函式庫</span></span></code></pre></div><p><strong>選擇原因</strong>：</p>
<ul>
<li>加密演算法需要經過審計的實現</li>
<li>效能關鍵</li>
<li>支援 PyPy（cffi 是 PyPy 官方推薦）</li>
</ul>
<h3 id="ruffpython-linter">ruff（Python Linter）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">ruff 架構：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── Python 封裝（ruff 套件）
</span></span><span class="line"><span class="ln">3</span><span class="cl">│   └── 提供 CLI 和簡單 API
</span></span><span class="line"><span class="ln">4</span><span class="cl">└── Rust 二進位
</span></span><span class="line"><span class="ln">5</span><span class="cl">    └── 實際的 lint 邏輯</span></span></code></pre></div><p><strong>選擇原因</strong>：</p>
<ul>
<li>追求極致速度（比 Flake8 快 10-100 倍）</li>
<li>Rust 的記憶體安全</li>
<li>作為獨立工具也可使用</li>
</ul>
<h3 id="mermaid-asciipypi-封裝">mermaid-ascii（PyPI 封裝）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">osl-packages/mermaid-ascii 架構：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── Python API（mermaid_ascii 模組）
</span></span><span class="line"><span class="ln">3</span><span class="cl">│   └── mermaid_to_ascii() 函式
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── subprocess 呼叫
</span></span><span class="line"><span class="ln">5</span><span class="cl">└── Go 編譯的 mermaid-ascii 二進位
</span></span><span class="line"><span class="ln">6</span><span class="cl">    └── Mermaid → ASCII 轉換</span></span></code></pre></div><p><strong>選擇原因</strong>：</p>
<ul>
<li>重用現有的 Go 實現</li>
<li>不需要 Node.js 依賴</li>
<li>提供 Python 友善的介面</li>
</ul>
<hr>
<h2 id="優點為什麼使用這種模式">【優點】為什麼使用這種模式？</h2>
<h3 id="1-效能">1. 效能</h3>
<p>核心邏輯用高效能語言實現，Python 只做介面：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 使用者感受不到底層是 Rust</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">import</span> <span class="nn">ruff</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 實際上是呼叫 Rust 編譯的二進位</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">ruff</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="s2">&#34;my_code.py&#34;</span><span class="p">)</span></span></span></code></pre></div><h3 id="2-重用現有實現">2. 重用現有實現</h3>
<p>不需要用 Python 重寫已經穩定的程式碼：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">情境：有一個優秀的 Go CLI 工具
</span></span><span class="line"><span class="ln">2</span><span class="cl">選項 A：用 Python 重寫（大量工作）
</span></span><span class="line"><span class="ln">3</span><span class="cl">選項 B：封裝 Go 二進位（少量工作）← 通常更好</span></span></code></pre></div><h3 id="3-跨語言生態整合">3. 跨語言生態整合</h3>
<p>讓 Python 使用者能夠使用其他語言的優秀工具：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Python 使用者不需要安裝 Go</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">from</span> <span class="nn">mermaid_ascii</span> <span class="kn">import</span> <span class="n">mermaid_to_ascii</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">diagram</span> <span class="o">=</span> <span class="n">mermaid_to_ascii</span><span class="p">(</span><span class="s2">&#34;graph LR; A--&gt;B&#34;</span><span class="p">)</span></span></span></code></pre></div><h3 id="4-維護分離">4. 維護分離</h3>
<p>核心邏輯與 Python 封裝可以獨立更新：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">mermaid-ascii（Go）: v0.6.1 → v0.7.0（核心更新）
</span></span><span class="line"><span class="ln">2</span><span class="cl">mermaid-ascii（PyPI）: 0.6.1 → 0.7.0（同步更新封裝）</span></span></code></pre></div><hr>
<h2 id="缺點這種模式的限制">【缺點】這種模式的限制</h2>
<h3 id="1-平台依賴">1. 平台依賴</h3>
<p>需要為每個作業系統和 CPU 架構提供預編譯二進位：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">典型的 wheel 矩陣：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── manylinux_x86_64
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── manylinux_aarch64
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── macosx_x86_64
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── macosx_arm64
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">└── win_amd64
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">缺點：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 維護成本高
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 新平台支援需要時間
</span></span><span class="line"><span class="ln">11</span><span class="cl">- CI/CD 複雜度增加</span></span></code></pre></div><h3 id="2-安裝體積">2. 安裝體積</h3>
<p>wheel 檔案較大（包含二進位）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">套件大小比較：
</span></span><span class="line"><span class="ln">2</span><span class="cl">純 Python 套件   ~50 KB
</span></span><span class="line"><span class="ln">3</span><span class="cl">封裝二進位套件   ~5-50 MB（依二進位大小）</span></span></code></pre></div><h3 id="3-除錯困難">3. 除錯困難</h3>
<p>錯誤可能發生在二進位層，難以追蹤：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 錯誤訊息可能是二進位的 stderr</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">some_binary_wrapper</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">except</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">CalledProcessError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="c1"># e.stderr 是二進位的錯誤訊息</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="c1"># 可能不是 Python 友善的格式</span></span></span></code></pre></div><h3 id="4-無法修改核心邏輯">4. 無法修改核心邏輯</h3>
<p>想改變底層行為必須重編譯核心：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">如果你需要：
</span></span><span class="line"><span class="ln">2</span><span class="cl">- 修改演算法
</span></span><span class="line"><span class="ln">3</span><span class="cl">- 加入自訂功能
</span></span><span class="line"><span class="ln">4</span><span class="cl">- 修復底層 bug
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl">你必須：
</span></span><span class="line"><span class="ln">7</span><span class="cl">1. 修改原始語言的程式碼
</span></span><span class="line"><span class="ln">8</span><span class="cl">2. 重新編譯
</span></span><span class="line"><span class="ln">9</span><span class="cl">3. 重新打包 wheel</span></span></code></pre></div><h3 id="5-供應鏈風險">5. 供應鏈風險</h3>
<p>二進位來源需要信任：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">風險考量：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 二進位是否來自可信來源？
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 是否有可驗證的建構流程？
</span></span><span class="line"><span class="ln">4</span><span class="cl">└── 是否有安全審計？
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl">最佳實踐：
</span></span><span class="line"><span class="ln">7</span><span class="cl">├── 使用知名維護者的套件
</span></span><span class="line"><span class="ln">8</span><span class="cl">├── 檢查 GitHub Actions 等 CI 建構紀錄
</span></span><span class="line"><span class="ln">9</span><span class="cl">└── 考慮自行建構（如果可能）</span></span></code></pre></div><hr>
<h2 id="比較純-python-vs-封裝二進位">【比較】純 Python vs 封裝二進位</h2>
<h3 id="特性比較表">特性比較表</h3>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>純 Python</th>
          <th>封裝預編譯二進位</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>效能</strong></td>
          <td>較慢</td>
          <td>可達原生速度</td>
      </tr>
      <tr>
          <td><strong>可移植性</strong></td>
          <td>極佳（任何有 Python 的地方）</td>
          <td>受限於預編譯平台</td>
      </tr>
      <tr>
          <td><strong>除錯</strong></td>
          <td>容易（Python 工具鏈）</td>
          <td>困難（跨語言）</td>
      </tr>
      <tr>
          <td><strong>修改靈活度</strong></td>
          <td>高（直接修改程式碼）</td>
          <td>低（需重新編譯）</td>
      </tr>
      <tr>
          <td><strong>安裝體積</strong></td>
          <td>小</td>
          <td>大（含二進位）</td>
      </tr>
      <tr>
          <td><strong>依賴管理</strong></td>
          <td>簡單</td>
          <td>複雜</td>
      </tr>
      <tr>
          <td><strong>透明度</strong></td>
          <td>完全可見</td>
          <td>部分黑箱</td>
      </tr>
      <tr>
          <td><strong>開發速度</strong></td>
          <td>快（Python 生態）</td>
          <td>需要多語言技能</td>
      </tr>
  </tbody>
</table>
<h3 id="決策流程">決策流程</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">選擇純 Python 如果：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 效能不是關鍵瓶頸
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 需要頻繁修改邏輯
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 需要最大可移植性
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├── 功能相對簡單
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">└── 希望保持程式碼透明
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">選擇封裝二進位如果：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 效能是關鍵需求
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 已有成熟的非 Python 實現
</span></span><span class="line"><span class="ln">11</span><span class="cl">├── 核心邏輯穩定，不常修改
</span></span><span class="line"><span class="ln">12</span><span class="cl">├── 需要系統級別的操作
</span></span><span class="line"><span class="ln">13</span><span class="cl">└── 安全性要求使用審計過的實現</span></span></code></pre></div><hr>
<h2 id="實作如何封裝二進位">【實作】如何封裝二進位</h2>
<h3 id="方法一subprocess-呼叫">方法一：subprocess 呼叫</h3>
<p>最簡單的封裝方式，適合 CLI 工具：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># my_wrapper/core.py</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">subprocess</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">import</span> <span class="nn">shutil</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">def</span> <span class="nf">find_binary</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="s2">&#34;&#34;&#34;找到封裝的二進位檔案&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1"># 二進位通常放在套件目錄內</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">package_dir</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">binary</span> <span class="o">=</span> <span class="n">package_dir</span> <span class="o">/</span> <span class="s2">&#34;bin&#34;</span> <span class="o">/</span> <span class="s2">&#34;my_tool&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="k">if</span> <span class="n">binary</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="k">return</span> <span class="n">binary</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="c1"># 或者在 PATH 中尋找</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="k">return</span> <span class="n">shutil</span><span class="o">.</span><span class="n">which</span><span class="p">(</span><span class="s2">&#34;my_tool&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">def</span> <span class="nf">run_tool</span><span class="p">(</span><span class="n">input_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="s2">&#34;&#34;&#34;呼叫封裝的工具&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">binary</span> <span class="o">=</span> <span class="n">find_binary</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">binary</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">        <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&#34;找不到 my_tool 二進位&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">binary</span><span class="p">)],</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="nb">input</span><span class="o">=</span><span class="n">input_text</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="n">capture_output</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="n">text</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="n">check</span><span class="o">=</span><span class="kc">True</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="k">return</span> <span class="n">result</span><span class="o">.</span><span class="n">stdout</span></span></span></code></pre></div><h3 id="方法二ctypes--cffi">方法二：ctypes / cffi</h3>
<p>適合函式庫（.so / .dll）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># my_wrapper/bindings.py</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">ctypes</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">def</span> <span class="nf">load_library</span><span class="p">():</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="s2">&#34;&#34;&#34;載入共享函式庫&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">package_dir</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="c1"># 根據平台選擇正確的檔案</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="kn">import</span> <span class="nn">platform</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">if</span> <span class="n">platform</span><span class="o">.</span><span class="n">system</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Darwin&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="n">lib_name</span> <span class="o">=</span> <span class="s2">&#34;libmy_tool.dylib&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">elif</span> <span class="n">platform</span><span class="o">.</span><span class="n">system</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;Windows&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="n">lib_name</span> <span class="o">=</span> <span class="s2">&#34;my_tool.dll&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="n">lib_name</span> <span class="o">=</span> <span class="s2">&#34;libmy_tool.so&#34;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">lib_path</span> <span class="o">=</span> <span class="n">package_dir</span> <span class="o">/</span> <span class="s2">&#34;lib&#34;</span> <span class="o">/</span> <span class="n">lib_name</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">return</span> <span class="n">ctypes</span><span class="o">.</span><span class="n">CDLL</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">lib_path</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"># 載入並設定函式簽名</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="n">_lib</span> <span class="o">=</span> <span class="n">load_library</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="n">_lib</span><span class="o">.</span><span class="n">process_data</span><span class="o">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="o">.</span><span class="n">c_char_p</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="n">_lib</span><span class="o">.</span><span class="n">process_data</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="o">.</span><span class="n">c_char_p</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">def</span> <span class="nf">process_data</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="s2">&#34;&#34;&#34;Python 友善的介面&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">_lib</span><span class="o">.</span><span class="n">process_data</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="k">return</span> <span class="n">result</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span></span></span></code></pre></div><h3 id="方法三使用專門工具">方法三：使用專門工具</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">推薦工具：
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── PyOxidizer：打包 Python + Rust
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── Briefcase：跨平台打包
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── Nuitka：Python → 原生編譯
</span></span><span class="line"><span class="ln">5</span><span class="cl">└── 自訂 GitHub Actions：建構多平台 wheel</span></span></code></pre></div><hr>
<h2 id="打包建立-wheel">【打包】建立 wheel</h2>
<h3 id="專案結構">專案結構</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">my_package/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── pyproject.toml
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── src/
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">│   └── my_package/
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│       ├── __init__.py
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">│       ├── core.py          # Python 封裝
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│       └── bin/             # 預編譯二進位
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">│           ├── my_tool-linux-x64
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│           ├── my_tool-darwin-arm64
</span></span><span class="line"><span class="ln">10</span><span class="cl">│           └── my_tool-windows-x64.exe
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── scripts/
</span></span><span class="line"><span class="ln">12</span><span class="cl">    └── build_binaries.sh    # 建構腳本</span></span></code></pre></div><h3 id="pyprojecttoml-設定">pyproject.toml 設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">[</span><span class="nx">build-system</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">requires</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;hatchling&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">build-backend</span> <span class="p">=</span> <span class="s2">&#34;hatchling.build&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;my-package&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;0.1.0&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Python wrapper for my_tool&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c"># 包含二進位檔案</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">include</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="s2">&#34;src/my_package/bin/*&#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c"># 設定平台特定的 wheel</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">hatch</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">wheel</span><span class="p">.</span><span class="nx">hooks</span><span class="p">.</span><span class="nx">custom</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c"># 自訂 hook 來處理平台特定二進位</span></span></span></code></pre></div><h3 id="github-actions-範例">GitHub Actions 範例</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/build.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheels</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">push, release]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">        </span><span class="nt">os</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ubuntu-latest, macos-latest, windows-latest]</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="nt">arch</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">x64, arm64]</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">${{ matrix.os }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build binary</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="sd">          # 根據目標平台建構二進位
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="sd">          ./scripts/build_binary.sh ${{ matrix.arch }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build wheel</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="sd">          pip install build
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="sd">          python -m build --wheel</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload wheel</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wheel-${{ matrix.os }}-${{ matrix.arch }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist/*.whl</span></span></span></code></pre></div><hr>
<h2 id="案例研究beautiful-mermaid-py">【案例研究】beautiful-mermaid-py</h2>
<h3 id="背景">背景</h3>
<p>將 Mermaid 圖表轉換為 ASCII 藝術的工具，存在多種實現：</p>
<table>
  <thead>
      <tr>
          <th>專案</th>
          <th>語言</th>
          <th>實現方式</th>
          <th>圖表支援</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>mermaid-ascii</td>
          <td>Go</td>
          <td>原創實現</td>
          <td>2 種</td>
      </tr>
      <tr>
          <td>beautiful-mermaid</td>
          <td>TypeScript</td>
          <td>從 Go 移植並擴展</td>
          <td>5 種</td>
      </tr>
      <tr>
          <td>beautiful-mermaid-py</td>
          <td>Python</td>
          <td>從 TypeScript 移植</td>
          <td>5 種</td>
      </tr>
      <tr>
          <td>osl-packages/mermaid-ascii</td>
          <td>Python</td>
          <td>封裝 Go 二進位</td>
          <td>2 種</td>
      </tr>
  </tbody>
</table>
<h3 id="兩種-python-方案比較">兩種 Python 方案比較</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">方案 A：封裝 Go 二進位（osl-packages）
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── 優點：效能較好、維護成本低
</span></span><span class="line"><span class="ln">3</span><span class="cl">├── 缺點：平台依賴、無法修改邏輯
</span></span><span class="line"><span class="ln">4</span><span class="cl">└── 適合：追求效能、不需自訂
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl">方案 B：純 Python 移植（beautiful-mermaid-py）
</span></span><span class="line"><span class="ln">7</span><span class="cl">├── 優點：無依賴、可自訂、跨平台
</span></span><span class="line"><span class="ln">8</span><span class="cl">├── 缺點：效能略低（但對此任務足夠）
</span></span><span class="line"><span class="ln">9</span><span class="cl">└── 適合：需要修改、追求簡潔</span></span></code></pre></div><h3 id="決策分析">決策分析</h3>
<p>對於 Mermaid ASCII 渲染這個需求：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">效能需求：低（渲染一次圖表不需要毫秒級優化）
</span></span><span class="line"><span class="ln">2</span><span class="cl">修改需求：可能（未來可能想客製化輸出格式）
</span></span><span class="line"><span class="ln">3</span><span class="cl">平台多樣性：高（不同開發環境）
</span></span><span class="line"><span class="ln">4</span><span class="cl">維護成本：純 Python 更低
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl">結論：對於這個場景，純 Python 是更好的選擇</span></span></code></pre></div><hr>
<h2 id="總結">總結</h2>
<h3 id="何時封裝二進位">何時封裝二進位</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">適合封裝二進位：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├── 效能關鍵的運算（加密、ML、圖像處理）
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├── 已有成熟的非 Python 實現
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 需要系統級別的操作
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── 安全性要求使用審計過的程式碼
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">不適合封裝二進位：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 簡單的文字處理或資料轉換
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 需要頻繁修改邏輯的功能
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 追求最大可移植性的工具
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── 功能用純 Python 就能達到足夠效能</span></span></code></pre></div><h3 id="架構選擇原則">架構選擇原則</h3>
<ol>
<li><strong>效能驅動</strong>：只有當效能是瓶頸時才考慮封裝二進位</li>
<li><strong>重用優先</strong>：有成熟實現時考慮封裝，否則考慮純 Python</li>
<li><strong>維護成本</strong>：評估長期維護的複雜度</li>
<li><strong>團隊技能</strong>：選擇團隊能夠維護的方案</li>
</ol>
<hr>
<h2 id="延伸閱讀">延伸閱讀</h2>
<ul>
<li><a href="https://packaging.python.org/en/latest/guides/packaging-binary-extensions/">Python Packaging Guide - Binary Extensions</a></li>
<li><a href="https://github.com/pypa/manylinux">manylinux 標準</a></li>
<li><a href="https://pyoxidizer.readthedocs.io/">PyOxidizer 文件</a></li>
<li><a href="/blog/python-advanced/05-c-extensions/" data-link-title="模組五：用 C 擴展 Python" data-link-desc="學習使用 ctypes、cffi、Cython、pybind11 擴展 Python">模組四：用 C 擴展 Python</a> - 另一種整合原生程式碼的方式</li>
</ul>
<hr>
<p><em>上一章：<a href="/blog/python-advanced/07-packaging/best-practices/" data-link-title="6.4 套件維護最佳實踐" data-link-desc="長期維護 Python 套件的最佳實踐">套件維護最佳實踐</a></em>
<em>下一模組：<a href="/blog/python-advanced/08-practical-optimization/" data-link-title="模組八：實戰效能優化" data-link-desc="將入門系列的並行處理與效能優化知識應用於真實系統">模組七：實戰效能優化</a></em></p>
]]></content:encoded></item><item><title>模組七：打包與發布</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/</guid><description>&lt;p>本模組介紹現代 Python 套件的打包標準與發布流程。&lt;/p>
&lt;h2 id="為什麼學習打包與發布">為什麼學習打包與發布？&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>完整生命週期&lt;/strong>：從開發到發布的完整流程&lt;/li>
&lt;li>&lt;strong>現代標準&lt;/strong>：pyproject.toml 與 PEP 標準&lt;/li>
&lt;li>&lt;strong>工具比較&lt;/strong>：setuptools、Poetry、Hatch 的選擇&lt;/li>
&lt;/ul>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>關鍵收穫&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/pyproject-toml/" data-link-title="6.1 pyproject.toml 完整指南" data-link-desc="理解現代 Python 套件的設定標準">7.1&lt;/a>&lt;/td>
 &lt;td>pyproject.toml 完整指南&lt;/td>
 &lt;td>現代設定標準&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">7.2&lt;/a>&lt;/td>
 &lt;td>建構系統比較&lt;/td>
 &lt;td>setuptools vs Poetry vs Hatch&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/distribution/" data-link-title="6.3 發布到 PyPI" data-link-desc="學習如何建構 wheel 並發布到 PyPI">7.3&lt;/a>&lt;/td>
 &lt;td>發布到 PyPI&lt;/td>
 &lt;td>建構與上傳流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/best-practices/" data-link-title="6.4 套件維護最佳實踐" data-link-desc="長期維護 Python 套件的最佳實踐">7.4&lt;/a>&lt;/td>
 &lt;td>最佳實踐&lt;/td>
 &lt;td>套件維護指南&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/bundled-binaries/" data-link-title="6.5 封裝預編譯二進位" data-link-desc="Python 套件如何封裝其他語言編譯的二進位檔案">7.5&lt;/a>&lt;/td>
 &lt;td>封裝預編譯二進位&lt;/td>
 &lt;td>Python 封裝 Go/Rust/C 二進位&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="相關-pep-標準">相關 PEP 標準&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>PEP 518&lt;/strong>：pyproject.toml 的 build-system 表&lt;/li>
&lt;li>&lt;strong>PEP 621&lt;/strong>：pyproject.toml 的 project 元數據&lt;/li>
&lt;li>&lt;strong>PEP 639&lt;/strong>：授權資訊的標準化&lt;/li>
&lt;/ul>
&lt;h2 id="先備知識">先備知識&lt;/h2>
&lt;ul>
&lt;li>基本的 Python 開發經驗&lt;/li>
&lt;li>了解 pip 和虛擬環境&lt;/li>
&lt;/ul>
&lt;h2 id="學習時間">學習時間&lt;/h2>
&lt;p>每章節約 30-45 分鐘，全模組約 2-3 小時&lt;/p>
&lt;hr>
&lt;p>上一模組：&lt;a href="https://tarrragon.github.io/blog/python-advanced/06-rust-extensions/" data-link-title="模組六：用 Rust 擴展 Python" data-link-desc="學習使用 PyO3 和 Maturin 用 Rust 擴展 Python">模組六：用 Rust 擴展 Python&lt;/a>&lt;/p></description><content:encoded><![CDATA[<p>本模組介紹現代 Python 套件的打包標準與發布流程。</p>
<h2 id="為什麼學習打包與發布">為什麼學習打包與發布？</h2>
<ul>
<li><strong>完整生命週期</strong>：從開發到發布的完整流程</li>
<li><strong>現代標準</strong>：pyproject.toml 與 PEP 標準</li>
<li><strong>工具比較</strong>：setuptools、Poetry、Hatch 的選擇</li>
</ul>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>關鍵收穫</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/python-advanced/07-packaging/pyproject-toml/" data-link-title="6.1 pyproject.toml 完整指南" data-link-desc="理解現代 Python 套件的設定標準">7.1</a></td>
          <td>pyproject.toml 完整指南</td>
          <td>現代設定標準</td>
      </tr>
      <tr>
          <td><a href="/blog/python-advanced/07-packaging/build-systems/" data-link-title="6.2 建構系統比較" data-link-desc="比較 setuptools、Poetry、Hatch 等建構系統">7.2</a></td>
          <td>建構系統比較</td>
          <td>setuptools vs Poetry vs Hatch</td>
      </tr>
      <tr>
          <td><a href="/blog/python-advanced/07-packaging/distribution/" data-link-title="6.3 發布到 PyPI" data-link-desc="學習如何建構 wheel 並發布到 PyPI">7.3</a></td>
          <td>發布到 PyPI</td>
          <td>建構與上傳流程</td>
      </tr>
      <tr>
          <td><a href="/blog/python-advanced/07-packaging/best-practices/" data-link-title="6.4 套件維護最佳實踐" data-link-desc="長期維護 Python 套件的最佳實踐">7.4</a></td>
          <td>最佳實踐</td>
          <td>套件維護指南</td>
      </tr>
      <tr>
          <td><a href="/blog/python-advanced/07-packaging/bundled-binaries/" data-link-title="6.5 封裝預編譯二進位" data-link-desc="Python 套件如何封裝其他語言編譯的二進位檔案">7.5</a></td>
          <td>封裝預編譯二進位</td>
          <td>Python 封裝 Go/Rust/C 二進位</td>
      </tr>
  </tbody>
</table>
<h2 id="相關-pep-標準">相關 PEP 標準</h2>
<ul>
<li><strong>PEP 518</strong>：pyproject.toml 的 build-system 表</li>
<li><strong>PEP 621</strong>：pyproject.toml 的 project 元數據</li>
<li><strong>PEP 639</strong>：授權資訊的標準化</li>
</ul>
<h2 id="先備知識">先備知識</h2>
<ul>
<li>基本的 Python 開發經驗</li>
<li>了解 pip 和虛擬環境</li>
</ul>
<h2 id="學習時間">學習時間</h2>
<p>每章節約 30-45 分鐘，全模組約 2-3 小時</p>
<hr>
<p>上一模組：<a href="/blog/python-advanced/06-rust-extensions/" data-link-title="模組六：用 Rust 擴展 Python" data-link-desc="學習使用 PyO3 和 Maturin 用 Rust 擴展 Python">模組六：用 Rust 擴展 Python</a></p>
]]></content:encoded></item><item><title>案例研究</title><link>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/</guid><description>&lt;p>本節收錄基於 &lt;code>.claude/lib&lt;/code> 實際程式碼的案例研究，展示如何將內部共用庫打包成可重用的 Python 套件。&lt;/p>
&lt;h2 id="案例列表">案例列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>案例&lt;/th>
 &lt;th>素材&lt;/th>
 &lt;th>學習重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/python-advanced/07-packaging/case-studies/package-library/" data-link-title="案例：打包共用庫" data-link-desc="將 .claude/lib 打包成可重用的 Python 套件">打包共用庫&lt;/a>&lt;/td>
 &lt;td>整個 lib 目錄&lt;/td>
 &lt;td>pyproject.toml、版本管理、發布流程&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="學習路徑">學習路徑&lt;/h2>
&lt;p>建議先完成本模組的理論章節，再閱讀案例研究：&lt;/p>
&lt;ol>
&lt;li>理解現代 Python 打包標準&lt;/li>
&lt;li>學習 pyproject.toml 設定&lt;/li>
&lt;li>通過案例實踐打包流程&lt;/li>
&lt;/ol></description><content:encoded><![CDATA[<p>本節收錄基於 <code>.claude/lib</code> 實際程式碼的案例研究，展示如何將內部共用庫打包成可重用的 Python 套件。</p>
<h2 id="案例列表">案例列表</h2>
<table>
  <thead>
      <tr>
          <th>案例</th>
          <th>素材</th>
          <th>學習重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/python-advanced/07-packaging/case-studies/package-library/" data-link-title="案例：打包共用庫" data-link-desc="將 .claude/lib 打包成可重用的 Python 套件">打包共用庫</a></td>
          <td>整個 lib 目錄</td>
          <td>pyproject.toml、版本管理、發布流程</td>
      </tr>
  </tbody>
</table>
<h2 id="學習路徑">學習路徑</h2>
<p>建議先完成本模組的理論章節，再閱讀案例研究：</p>
<ol>
<li>理解現代 Python 打包標準</li>
<li>學習 pyproject.toml 設定</li>
<li>通過案例實踐打包流程</li>
</ol>
]]></content:encoded></item></channel></rss>