當代軟體開發已經從單打獨鬥演變為團隊協作,Python 生態系統的開放特性讓套件發布成為開發者日常工作中無法迴避的環節。一個設計良好的套件不僅能提升程式碼的重複利用價值,更能在開源社群中建立專業形象。然而在台灣的開發實務中,許多工程師常常低估了套件發布的複雜度,導致使用者在安裝時遭遇版本衝突、文件不全或依賴關係混亂等問題。

專業的套件發布流程必須同時兼顧三個核心面向:清晰完整的技術文件讓使用者能快速理解套件功能、標準化的封裝流程確保跨平台相容性、一致的依賴管理避免環境差異造成的執行問題。這三個面向環環相扣,缺一不可。在接下來的內容中,我將從實務角度分享如何建立完整的 Python 套件發布工作流程。

Sphinx 技術文件建立實務

技術文件的品質直接影響套件的採用率,在開源社群中更是如此。使用者通常會在評估套件時首先查看文件是否完整,若發現說明不清或範例缺失,往往會直接選擇其他替代方案。Sphinx 作為 Python 社群的標準文件工具,提供了從原始碼註解自動生成 API 文件的能力,大幅降低文件維護成本。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

package "Sphinx 文件生成流程" {
  [原始碼 docstring] as source
  [reStructuredText 文件] as rst
  [Sphinx 建置引擎] as sphinx
  [HTML 輸出] as html
  [PDF 輸出] as pdf
  [主題樣式] as theme
  
  source --> sphinx
  rst --> sphinx
  theme --> sphinx
  sphinx --> html
  sphinx --> pdf
}

@enduml

Sphinx 採用 reStructuredText 標記語言作為文件格式基礎,相較於 Markdown 提供了更豐富的結構化元素。在實際應用中,reStructuredText 支援複雜的表格、數學公式、程式碼高亮顯示以及交叉參照功能,這些特性對於技術文件的專業呈現至關重要。此外 Sphinx 內建的自動索引功能能夠建立完整的 API 參考文件,讓使用者能快速查找特定函式或類別的說明。

在建立 Sphinx 文件專案時,首先需要安裝必要的套件並初始化文件目錄結構。透過執行 pip install sphinx 完成安裝後,可以在專案根目錄建立獨立的文件目錄,通常命名為 docs。進入該目錄後執行 sphinx-quickstart 指令,系統會引導完成基本設定,包括專案名稱、作者資訊、版本號等中繼資料。

# docs/conf.py 設定範例
# Sphinx 組態檔案,定義文件生成的各項參數

import os
import sys
sys.path.insert(0, os.path.abspath('..'))

# 專案基本資訊設定
project = 'Python 套件管理工具'
copyright = '2025, 玄貓(BlackCat)'
author = '玄貓(BlackCat)'
version = '1.0'
release = '1.0.0'

# Sphinx 擴充功能設定
extensions = [
    'sphinx.ext.autodoc',      # 自動從程式碼產生文件
    'sphinx.ext.napoleon',     # 支援 Google 與 NumPy 風格的 docstring
    'sphinx.ext.viewcode',     # 在文件中加入原始碼連結
    'sphinx.ext.intersphinx',  # 跨專案文件參照
]

# 文件主題設定
html_theme = 'sphinx_rtd_theme'  # 使用 Read the Docs 主題
html_static_path = ['_static']

# 支援繁體中文搜尋
language = 'zh_TW'

撰寫 reStructuredText 文件時需要注意格式規範,標題層級透過不同符號表示,程式碼區塊使用 code-block 指令標記,並可指定程式語言以啟用語法高亮。在文件中引用 API 時,Sphinx 提供專門的角色標記,例如使用 :func: 引用函式、:class: 引用類別,這些標記會自動產生連結指向對應的 API 文件。

# mypackage/core.py 程式碼範例
# 示範如何撰寫符合 Sphinx 規範的 docstring

class DataProcessor:
    """資料處理核心類別
    
    此類別提供資料清理、轉換與驗證功能,適用於各類結構化資料處理場景。
    
    Parameters
    ----------
    config : dict
        處理器組態設定,包含驗證規則與轉換參數
    logger : logging.Logger, optional
        日誌記錄器實例,預設為 None
        
    Attributes
    ----------
    rules : list
        資料驗證規則清單
    transformers : dict
        資料轉換器映射表
        
    Examples
    --------
    建立資料處理器並執行基本操作:
    
    >>> processor = DataProcessor({'validate': True})
    >>> result = processor.process(raw_data)
    >>> print(result.status)
    'success'
    """
    
    def __init__(self, config, logger=None):
        self.config = config
        self.logger = logger
        self.rules = []
        self.transformers = {}
    
    def process(self, data):
        """處理輸入資料
        
        對輸入資料執行驗證與轉換操作,回傳處理結果物件。
        
        Parameters
        ----------
        data : pd.DataFrame
            待處理的原始資料
            
        Returns
        -------
        ProcessResult
            包含處理狀態與結果資料的物件
            
        Raises
        ------
        ValidationError
            當資料驗證失敗時拋出
        TransformError
            當資料轉換失敗時拋出
        """
        # 實際處理邏輯
        pass

文件建置完成後,執行 make html 會在 _build/html 目錄生成靜態網站,可直接部署到文件託管服務。若需要 PDF 格式,則執行 make latexpdf 產生 LaTeX 中間檔案並編譯為 PDF。在實務上建議將文件建置整合到持續整合流程中,每次程式碼提交時自動更新文件,確保文件與程式碼同步。

Setuptools 封裝標準化流程

套件封裝是將 Python 專案轉換為可發布格式的關鍵步驟,Setuptools 提供了標準化的封裝機制,讓套件能在不同環境中一致安裝。封裝格式主要分為原始碼發布與輪子發布兩種,原始碼發布包含完整的程式碼檔案,需要在目標系統上編譯安裝,而輪子發布則是預編譯的二進位套件,安裝速度更快且無需編譯環境。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

package "Setuptools 封裝流程" {
  [setup.py 設定檔] as setup
  [專案原始碼] as code
  [MANIFEST.in] as manifest
  [原始碼發布 sdist] as sdist
  [輪子發布 wheel] as wheel
  [PyPI 套件庫] as pypi
  
  setup --> sdist
  setup --> wheel
  code --> sdist
  code --> wheel
  manifest --> sdist
  sdist --> pypi
  wheel --> pypi
}

note right of setup
  定義套件中繼資料
  指定依賴套件
  設定入口點
end note

@enduml

建立 setup.py 檔案是封裝流程的起點,這個 Python 腳本描述了套件的所有中繼資料,包括名稱、版本、作者資訊、授權條款以及依賴關係。在撰寫時需要特別注意版本號的規範,Python 社群建議採用語意化版本控制,格式為主版本號.次版本號.修訂號,其中主版本號變更代表不相容的 API 修改,次版本號變更代表向下相容的功能新增,修訂號變更則是錯誤修正。

# setup.py 完整設定範例
from setuptools import setup, find_packages
import os

# 讀取 README 作為長描述
with open('README.md', 'r', encoding='utf-8') as f:
    long_description = f.read()

# 讀取依賴清單
with open('requirements.txt', 'r', encoding='utf-8') as f:
    requirements = [line.strip() for line in f if line.strip() and not line.startswith('#')]

setup(
    name='python-toolkit',
    version='1.0.0',
    author='玄貓(BlackCat)',
    author_email='blackcat@example.com',
    description='Python 開發工具集,提供資料處理與系統整合功能',
    long_description=long_description,
    long_description_content_type='text/markdown',
    url='https://github.com/blackcat/python-toolkit',
    project_urls={
        'Documentation': 'https://python-toolkit.readthedocs.io',
        'Bug Reports': 'https://github.com/blackcat/python-toolkit/issues',
        'Source Code': 'https://github.com/blackcat/python-toolkit',
    },
    
    # 套件發現與包含
    packages=find_packages(exclude=['tests', 'docs', 'examples']),
    include_package_data=True,
    
    # 依賴管理
    install_requires=requirements,
    extras_require={
        'dev': [
            'pytest>=7.0.0',
            'pytest-cov>=4.0.0',
            'flake8>=5.0.0',
            'black>=22.0.0',
        ],
        'docs': [
            'sphinx>=5.0.0',
            'sphinx-rtd-theme>=1.0.0',
        ],
    },
    
    # Python 版本需求
    python_requires='>=3.8',
    
    # 分類資訊
    classifiers=[
        'Development Status :: 5 - Production/Stable',
        'Intended Audience :: Developers',
        'Topic :: Software Development :: Libraries :: Python Modules',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.8',
        'Programming Language :: Python :: 3.9',
        'Programming Language :: Python :: 3.10',
        'Programming Language :: Python :: 3.11',
        'Operating System :: OS Independent',
    ],
    
    # 命令列工具入口點
    entry_points={
        'console_scripts': [
            'pytoolkit=python_toolkit.cli:main',
        ],
    },
    
    # 套件資料
    package_data={
        'python_toolkit': ['config/*.yaml', 'templates/*.jinja2'],
    },
)

MANIFEST.in 檔案用於指定額外需要包含在發布套件中的檔案,例如授權文件、設定範例、靜態資源等。Setuptools 預設只會包含 Python 原始碼檔案,其他類型的檔案必須透過 MANIFEST.in 明確指定。在實務上建議將所有文件、範例程式碼以及測試資料都納入發布套件,讓使用者能獲得完整的參考資料。

# MANIFEST.in 檔案範例
# 指定需要包含在發布套件中的額外檔案

include LICENSE
include README.md
include CHANGELOG.md
include requirements.txt
include requirements-dev.txt

recursive-include python_toolkit/config *.yaml *.yml
recursive-include python_toolkit/templates *.jinja2
recursive-include docs *.rst *.md
recursive-include examples *.py *.ipynb
recursive-include tests *.py

global-exclude __pycache__
global-exclude *.py[cod]
global-exclude .DS_Store

完成 setup.pyMANIFEST.in 設定後,可以透過 python setup.py sdist bdist_wheel 指令同時建立原始碼發布與輪子發布。生成的檔案會放在 dist 目錄中,檔名格式為套件名稱加上版本號。在正式發布前建議先在本地環境測試安裝,確認套件能正確安裝且所有依賴都能正常解析。

# 建立發布套件的完整流程
# 清理舊的建置檔案
rm -rf build/ dist/ *.egg-info

# 更新建置工具
python -m pip install --upgrade setuptools wheel twine

# 建立發布套件
python setup.py sdist bdist_wheel

# 檢查套件完整性
twine check dist/*

# 本地測試安裝
pip install dist/python-toolkit-1.0.0-py3-none-any.whl

# 上傳至 PyPI
twine upload dist/*

上傳套件到 PyPI 需要先註冊帳號並設定 API 金鑰,使用 Twine 工具可以確保上傳過程使用 HTTPS 加密連線。在首次發布前建議先上傳到 TestPyPI 測試環境進行驗證,確認套件資訊顯示正確、依賴關係完整且能順利安裝。正式發布後套件版本號無法修改,因此版本號的管理需要特別謹慎。

# 版本號管理策略範例
# 建議將版本號定義在獨立模組中統一管理

# python_toolkit/__version__.py
__version__ = '1.0.0'
__version_info__ = tuple(int(i) for i in __version__.split('.'))

# setup.py 中引用版本號
from python_toolkit.__version__ import __version__

setup(
    name='python-toolkit',
    version=__version__,
    # 其他設定...
)

Pipenv 依賴管理實踐

依賴管理的一致性直接影響專案的穩定性與團隊協作效率,當不同開發者使用不同版本的依賴套件時,程式碼在某些環境能正常執行,在其他環境卻出現異常。Pipenv 整合了 pip 與 virtualenv 的功能,提供統一的介面管理虛擬環境與依賴套件,透過 Pipfile 與 Pipfile.lock 兩個檔案確保環境一致性。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

package "Pipenv 依賴管理架構" {
  [Pipfile 設定檔] as pipfile
  [Pipfile.lock 鎖定檔] as lock
  [虛擬環境] as venv
  [PyPI 套件源] as pypi
  [本地開發環境] as dev
  
  pipfile --> lock : 生成鎖定版本
  lock --> venv : 建立環境
  pypi --> venv : 下載套件
  venv --> dev : 啟動環境
}

note right of lock
  記錄確切版本雜湊值
  確保環境可重現性
  防止依賴衝突
end note

@enduml

Pipfile 採用 TOML 格式,相較於傳統的 requirements.txt 提供更清晰的結構與更豐富的功能。檔案分為多個區段,packages 區段定義生產環境依賴,dev-packages 區段定義開發環境依賴,source 區段則指定套件來源。這種分層設計讓開發者能精確控制不同環境的依賴配置,避免將開發工具納入生產環境。

# Pipfile 完整設定範例
# 定義專案的依賴套件與環境設定

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

# 生產環境依賴
[packages]
requests = "==2.31.0"
numpy = ">=1.24.0,<2.0.0"
pandas = "~=2.0.0"
sqlalchemy = "*"
python-dotenv = "==1.0.0"

# 開發環境依賴
[dev-packages]
pytest = "==7.4.0"
pytest-cov = "==4.1.0"
black = "==23.7.0"
flake8 = "==6.1.0"
mypy = "==1.5.0"
ipython = "*"
jupyter = "*"

# Python 版本需求
[requires]
python_version = "3.11"

# Pipenv 行為設定
[pipenv]
allow_prereleases = false

Pipfile.lock 是由 Pipenv 自動生成的檔案,記錄了每個依賴套件的確切版本號、雜湊值以及傳遞性依賴關係。這個檔案確保團隊成員或持續整合環境能安裝完全相同的套件版本,避免因版本差異導致的執行問題。在版本控制系統中應該同時提交 Pipfile 與 Pipfile.lock,讓其他開發者能重現相同的環境。

# Pipenv 常用操作指令集
# 建立虛擬環境並安裝依賴
pipenv install

# 安裝開發環境依賴
pipenv install --dev

# 新增套件到生產環境
pipenv install requests

# 新增套件到開發環境
pipenv install --dev pytest

# 更新所有套件到最新相容版本
pipenv update

# 檢查套件安全性漏洞
pipenv check

# 顯示依賴關係樹狀圖
pipenv graph

# 啟動虛擬環境 shell
pipenv shell

# 在虛擬環境中執行指令
pipenv run python script.py

# 移除虛擬環境
pipenv --rm

# 產生 requirements.txt 相容格式
pipenv requirements > requirements.txt

在實際專案中建議建立標準化的環境設定流程,新進開發者只需執行幾個簡單指令即可完成環境準備。將環境設定步驟記錄在 README 檔案中,搭配 Makefile 或腳本自動化常用操作,能大幅降低新成員的上手門檻。

# 環境設定自動化腳本範例
# scripts/setup_env.py

import subprocess
import sys
import os

def check_python_version():
    """檢查 Python 版本是否符合需求"""
    required_version = (3, 8)
    current_version = sys.version_info[:2]
    
    if current_version < required_version:
        print(f"錯誤:需要 Python {required_version[0]}.{required_version[1]} 或更高版本")
        print(f"目前版本:Python {current_version[0]}.{current_version[1]}")
        sys.exit(1)
    
    print(f"Python 版本檢查通過:{sys.version}")

def install_pipenv():
    """安裝 Pipenv 工具"""
    try:
        subprocess.run(['pipenv', '--version'], check=True, capture_output=True)
        print("Pipenv 已安裝")
    except (subprocess.CalledProcessError, FileNotFoundError):
        print("正在安裝 Pipenv...")
        subprocess.run([sys.executable, '-m', 'pip', 'install', 'pipenv'], check=True)
        print("Pipenv 安裝完成")

def setup_environment():
    """建立虛擬環境並安裝依賴"""
    print("正在建立虛擬環境...")
    subprocess.run(['pipenv', 'install', '--dev'], check=True)
    print("環境設定完成")
    
    print("\n執行以下指令啟動虛擬環境:")
    print("  pipenv shell")

if __name__ == '__main__':
    print("開始設定專案環境...\n")
    check_python_version()
    install_pipenv()
    setup_environment()
    print("\n環境設定完成!")

Conda 多語言環境管理

Conda 提供的環境管理能力超越了 Python 的範疇,支援 C、C++、R 等多種語言的套件管理,特別適合資料科學與機器學習專案。這類專案通常需要整合多個程式語言的函式庫,例如使用 C++ 撰寫的高效能計算核心搭配 Python 介面,Conda 能統一管理這些跨語言依賴。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

package "Conda 環境管理體系" {
  [environment.yml 定義檔] as env
  [Conda 環境] as conda_env
  [Anaconda 套件庫] as anaconda
  [Conda-forge 社群庫] as forge
  [PyPI 套件庫] as pypi
  [專案執行環境] as runtime
  
  env --> conda_env : 建立環境
  anaconda --> conda_env : 官方套件
  forge --> conda_env : 社群套件
  pypi --> conda_env : Python 套件
  conda_env --> runtime : 啟動環境
}

note right of conda_env
  支援多語言套件
  跨平台相容性
  自動解決依賴衝突
end note

@enduml

environment.yml 檔案是 Conda 環境定義的核心,採用 YAML 格式描述環境名稱、通道來源以及依賴套件清單。Conda 通道是套件的發布來源,預設使用 Anaconda 官方通道,但社群維護的 conda-forge 通道通常提供更新更全面的套件版本。在設定通道順序時需要注意優先權,位於清單前面的通道會優先搜尋套件。

# environment.yml 完整設定範例
# 定義跨語言專案的 Conda 環境

name: data-science-project
channels:
  - conda-forge
  - defaults

dependencies:
  # Python 直譯器
  - python=3.11
  
  # 核心科學計算套件
  - numpy=1.24.*
  - scipy=1.11.*
  - pandas=2.0.*
  
  # 資料視覺化
  - matplotlib=3.7.*
  - seaborn=0.12.*
  - plotly=5.15.*
  
  # 機器學習框架
  - scikit-learn=1.3.*
  - tensorflow=2.13.*
  - pytorch=2.0.*
  
  # 資料處理工具
  - jupyter=1.0.*
  - jupyterlab=4.0.*
  - notebook=7.0.*
  
  # C/C++ 編譯工具鏈
  - gcc_linux-64=11.4.*
  - gxx_linux-64=11.4.*
  - cmake=3.27.*
  
  # 系統層級依賴
  - hdf5=1.14.*
  - netcdf4=1.6.*
  
  # 透過 pip 安裝的 Python 套件
  - pip
  - pip:
    - opencv-python==4.8.0.76
    - pillow==10.0.0
    - python-dotenv==1.0.0

# 環境變數設定
variables:
  CUDA_HOME: /usr/local/cuda
  LD_LIBRARY_PATH: /usr/local/cuda/lib64:$LD_LIBRARY_PATH

Conda 環境的建立與啟動流程相對直觀,透過 conda env create 指令讀取 environment.yml 並自動安裝所有依賴。與 Pipenv 不同,Conda 環境不綁定特定專案目錄,而是集中管理在 Conda 安裝目錄下,這意味著同一個環境可以被多個專案共用,但也需要更謹慎地管理環境命名以避免衝突。

# Conda 環境管理完整指令
# 從 environment.yml 建立環境
conda env create -f environment.yml

# 啟動指定環境
conda activate data-science-project

# 顯示所有環境清單
conda env list

# 更新環境中的套件
conda env update -f environment.yml --prune

# 匯出目前環境設定
conda env export > environment.yml
conda env export --no-builds > environment.yml

# 複製環境
conda create --name new-env --clone data-science-project

# 移除環境
conda env remove --name data-science-project

# 清理快取釋放空間
conda clean --all

# 檢視環境中已安裝套件
conda list

# 搜尋可用套件
conda search numpy

# 安裝新套件到目前環境
conda install matplotlib

# 更新單一套件
conda update numpy

在企業環境中經常需要建立私有的 Conda 通道,用於發布內部開發的套件或快取外部套件以提升安裝速度。Conda 支援從本地目錄、網路伺服器或物件儲存服務建立自訂通道,搭配 conda-build 工具可以將自行開發的套件封裝為 Conda 格式並發布到私有通道。

# 企業級 Conda 設定範例
# .condarc 檔案位於使用者家目錄

channels:
  - https://conda.company.internal/packages  # 企業私有通道
  - conda-forge                               # 社群通道
  - defaults                                  # 官方通道

channel_priority: strict  # 嚴格遵守通道優先順序

# 預設安裝位置
envs_dirs:
  - /data/conda/envs
  - ~/.conda/envs

# 套件快取位置
pkgs_dirs:
  - /data/conda/pkgs
  - ~/.conda/pkgs

# 代理伺服器設定
proxy_servers:
  http: http://proxy.company.internal:8080
  https: https://proxy.company.internal:8080

# SSL 驗證設定
ssl_verify: true
ssl_verify_certificates: /etc/ssl/certs/ca-bundle.crt

# 自動啟動環境
auto_activate_base: false

# 顯示通道 URL
show_channel_urls: true

整合 Sphinx 與 Setuptools 的自動化流程

將文件建置整合到套件發布流程能確保每次發布都包含最新的文件,使用者能在套件安裝後直接查閱本地文件。Setuptools 提供擴充機制讓開發者自訂建置步驟,透過實作自訂命令可以在封裝過程中自動執行 Sphinx 文件建置。

# setup.py 整合 Sphinx 文件建置範例
from setuptools import setup, Command
from setuptools.command.build_py import build_py
import subprocess
import os
import shutil

class BuildSphinxCommand(Command):
    """自訂命令:建置 Sphinx 文件"""
    
    description = '建置 Sphinx HTML 文件'
    user_options = []
    
    def initialize_options(self):
        pass
    
    def finalize_options(self):
        pass
    
    def run(self):
        """執行 Sphinx 文件建置流程"""
        docs_dir = os.path.join(os.path.dirname(__file__), 'docs')
        build_dir = os.path.join(docs_dir, '_build')
        
        # 清理舊的建置檔案
        if os.path.exists(build_dir):
            shutil.rmtree(build_dir)
        
        # 執行 Sphinx 建置
        subprocess.run(
            ['sphinx-build', '-b', 'html', docs_dir, os.path.join(build_dir, 'html')],
            check=True
        )
        
        print(f"文件已建置完成:{os.path.join(build_dir, 'html')}")

class CustomBuildPy(build_py):
    """擴充標準建置命令,加入文件建置步驟"""
    
    def run(self):
        # 先執行標準建置流程
        build_py.run(self)
        
        # 執行文件建置
        self.run_command('build_sphinx')

setup(
    name='python-toolkit',
    version='1.0.0',
    
    # 註冊自訂命令
    cmdclass={
        'build_sphinx': BuildSphinxCommand,
        'build_py': CustomBuildPy,
    },
    
    # 將建置的文件包含在套件中
    package_data={
        'python_toolkit': ['docs/_build/html/*'],
    },
)

持續整合環境中的文件自動化發布能進一步提升開發效率,當程式碼推送到版本控制系統時自動觸發文件建置並部署到託管服務。Read the Docs 是常用的文件託管平台,支援從 Git 倉庫自動拉取程式碼並建置 Sphinx 文件,還提供版本管理與搜尋功能。

# .readthedocs.yml 設定範例
# Read the Docs 平台的建置設定檔

version: 2

build:
  os: ubuntu-22.04
  tools:
    python: "3.11"
  
  jobs:
    pre_build:
      - pip install poetry
      - poetry export -f requirements.txt --output requirements.txt --without-hashes

sphinx:
  configuration: docs/conf.py
  fail_on_warning: false

formats:
  - pdf
  - epub

python:
  install:
    - requirements: requirements.txt
    - requirements: docs/requirements.txt
    - method: pip
      path: .

版本控制與協作流程最佳實踐

版本控制系統是團隊協作的基礎設施,Git 的分支模型讓多位開發者能同時進行功能開發而不互相干擾。在 Python 專案中建議採用 Git Flow 或 GitHub Flow 等標準化工作流程,明確定義主分支、開發分支、功能分支的用途與合併策略。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

package "Git 協作流程" {
  [主分支 main] as main
  [開發分支 develop] as develop
  [功能分支 feature] as feature
  [修正分支 hotfix] as hotfix
  [程式碼審查] as review
  [自動化測試] as test
  [文件建置] as docs
  [套件發布] as release
  
  feature --> develop : 合併功能
  develop --> main : 發布版本
  main --> hotfix : 緊急修正
  hotfix --> main : 修正合併
  review --> develop : 審查通過
  develop --> test : 觸發測試
  test --> docs : 建置文件
  main --> release : 發布套件
}

note right of review
  強制程式碼審查
  確保程式碼品質
  知識分享機制
end note

@enduml

在協作開發過程中版本號的管理需要遵循一致的規範,語意化版本控制提供了清晰的版本號規則。當進行不相容的 API 變更時遞增主版本號,當以向下相容的方式新增功能時遞增次版本號,當進行向下相容的錯誤修正時遞增修訂號。搭配版本標籤與變更日誌能讓使用者清楚了解每個版本的變動內容。

# 自動化版本號管理腳本範例
# scripts/bump_version.py

import re
import sys
from pathlib import Path

def read_version(file_path):
    """從檔案中讀取目前版本號"""
    content = file_path.read_text(encoding='utf-8')
    match = re.search(r"__version__\s*=\s*['\"]([^'\"]+)['\"]", content)
    if not match:
        raise ValueError(f"無法在 {file_path} 中找到版本號")
    return match.group(1)

def parse_version(version_str):
    """解析版本號為數字元組"""
    parts = version_str.split('.')
    if len(parts) != 3:
        raise ValueError(f"版本號格式錯誤:{version_str}")
    return tuple(int(p) for p in parts)

def bump_version(version, bump_type):
    """遞增版本號
    
    bump_type 可為 'major'、'minor' 或 'patch'
    """
    major, minor, patch = parse_version(version)
    
    if bump_type == 'major':
        return f"{major + 1}.0.0"
    elif bump_type == 'minor':
        return f"{major}.{minor + 1}.0"
    elif bump_type == 'patch':
        return f"{major}.{minor}.{patch + 1}"
    else:
        raise ValueError(f"不支援的遞增類型:{bump_type}")

def update_version_file(file_path, new_version):
    """更新版本號檔案"""
    content = file_path.read_text(encoding='utf-8')
    new_content = re.sub(
        r"(__version__\s*=\s*['\"])[^'\"]+(['\"])",
        f"\\g<1>{new_version}\\g<2>",
        content
    )
    file_path.write_text(new_content, encoding='utf-8')
    print(f"已更新 {file_path} 版本號為 {new_version}")

def main():
    if len(sys.argv) != 2:
        print("使用方式:python bump_version.py [major|minor|patch]")
        sys.exit(1)
    
    bump_type = sys.argv[1]
    version_file = Path('python_toolkit/__version__.py')
    
    current_version = read_version(version_file)
    new_version = bump_version(current_version, bump_type)
    update_version_file(version_file, new_version)
    
    print(f"版本號已從 {current_version} 更新為 {new_version}")

if __name__ == '__main__':
    main()

程式碼審查是確保程式碼品質的重要環節,透過同儕審查能發現潛在的錯誤、改善程式碼可讀性並促進團隊知識分享。在 GitHub 或 GitLab 等平台上可以要求功能分支必須經過審查才能合併到主分支,搭配自動化的程式碼檢查工具如 Flake8、Black、Mypy 能在審查前就過濾掉格式問題與型別錯誤。

# .github/workflows/ci.yml 持續整合設定範例
# GitHub Actions 自動化測試與檢查流程

name: 持續整合

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  test:
    name: 執行測試與程式碼檢查
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.8', '3.9', '3.10', '3.11']
    
    steps:
      - name: 取出程式碼
        uses: actions/checkout@v3
      
      - name: 設定 Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: 快取依賴套件
        uses: actions/cache@v3
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-
      
      - name: 安裝依賴
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
      
      - name: 程式碼風格檢查
        run: |
          flake8 python_toolkit tests
          black --check python_toolkit tests
      
      - name: 型別檢查
        run: |
          mypy python_toolkit
      
      - name: 執行測試
        run: |
          pytest tests/ --cov=python_toolkit --cov-report=xml
      
      - name: 上傳覆蓋率報告
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml
          fail_ci_if_error: true
  
  docs:
    name: 建置文件
    runs-on: ubuntu-latest
    
    steps:
      - name: 取出程式碼
        uses: actions/checkout@v3
      
      - name: 設定 Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: 安裝依賴
        run: |
          pip install -r docs/requirements.txt
      
      - name: 建置 Sphinx 文件
        run: |
          cd docs
          make html
      
      - name: 部署到 GitHub Pages
        if: github.ref == 'refs/heads/main'
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs/_build/html

安全性與依賴更新策略

套件的依賴管理不僅關乎功能完整性,更涉及安全性維護。已知的安全漏洞會定期在各類安全資料庫中公布,使用含有漏洞的套件版本會讓專案暴露在風險之中。定期檢查依賴套件的安全狀態並及時更新是維護專案健康的必要工作。

# 安全性檢查工具使用範例
# 使用 pip-audit 掃描 Python 套件漏洞
pip install pip-audit
pip-audit

# 使用 safety 檢查已知安全問題
pip install safety
safety check

# Pipenv 內建安全檢查
pipenv check

# 更新有安全問題的套件
pip install --upgrade package-name
pipenv update package-name

在企業環境中經常需要建立依賴更新的審查機制,不能直接採用最新版本而是需要經過測試驗證。可以透過 Dependabot 等自動化工具定期檢查依賴更新,當有新版本發布時自動建立 Pull Request,經過自動化測試與人工審查後再決定是否合併。

# .github/dependabot.yml 自動化依賴更新設定
# Dependabot 會定期檢查並建立更新 PR

version: 2
updates:
  # Python 依賴更新
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
    open-pull-requests-limit: 10
    reviewers:
      - "blackcat"
    labels:
      - "dependencies"
      - "python"
    commit-message:
      prefix: "chore"
      include: "scope"
    
    # 依賴群組設定
    groups:
      production-dependencies:
        patterns:
          - "requests"
          - "numpy"
          - "pandas"
      development-dependencies:
        patterns:
          - "pytest*"
          - "black"
          - "flake8"
    
    # 忽略特定更新
    ignore:
      - dependency-name: "django"
        update-types: ["version-update:semver-major"]

套件發布完成後的維護同樣重要,需要建立清晰的問題回報管道與社群互動機制。在 GitHub 上設定 Issue 模板能引導使用者提供完整的問題資訊,Pull Request 模板則能確保貢獻者遵循專案的貢獻指南。建立 CODE_OF_CONDUCT 與 CONTRIBUTING 檔案能營造友善的社群氛圍。

# CONTRIBUTING.md 貢獻指南範例

# 貢獻指南

感謝您考慮為本專案做出貢獻!我們歡迎各種形式的貢獻,包括錯誤回報、功能建議、文件改善與程式碼貢獻。

## 開發環境設定

1. Fork 本專案並 clone 到本地:
   ```bash
   git clone https://github.com/your-username/python-toolkit.git
   cd python-toolkit
  1. 建立虛擬環境並安裝依賴:

    pipenv install --dev
    pipenv shell
    
  2. 安裝 pre-commit hooks:

    pre-commit install
    

開發流程

  1. 建立功能分支:

    git checkout -b feature/your-feature-name
    
  2. 撰寫程式碼並確保通過所有檢查:

    pytest tests/
    flake8 python_toolkit tests
    black python_toolkit tests
    mypy python_toolkit
    
  3. 撰寫或更新相關測試與文件

  4. 提交變更並推送到您的 fork:

    git add .
    git commit -m "feat: add new feature"
    git push origin feature/your-feature-name
    
  5. 建立 Pull Request 並詳細描述變更內容

程式碼風格

本專案遵循以下程式碼風格規範:

  • 使用 Black 格式化 Python 程式碼
  • 使用 Flake8 檢查程式碼品質
  • 使用 Mypy 進行型別檢查
  • docstring 採用 NumPy 風格

測試要求

所有新增功能必須包含對應的單元測試,測試覆蓋率目標為 90% 以上。

文件要求

公開 API 必須包含完整的 docstring,包括參數說明、回傳值說明、使用範例以及可能拋出的例外。