現代 Python 專案開發中,pyproject.toml 已成為管理版本、依賴和建置設定的標準。本文介紹如何利用 pyproject.toml、tox 與 Poetry 等工具,實作自動化版本管理、依賴宣告、二進位套件的建置與釋出,並整合 tox 進行自動化測試與建構。透過 pyproject.toml 中的設定,可以精確控制專案的版本資訊,並使用 importlib.metadata 動態讀取版本,避免多處維護版本資訊的困擾。同時,本文也涵蓋了使用 python -m build 建置 wheel 和原始碼發行版,以及處理包含原生擴充套件的建置流程。此外,文章也詳細說明瞭 manylinux wheel 的建置與釋出技巧,確保套件在不同 Linux 發行版上的相容性。最後,本文介紹了 tox 和 Poetry 的使用,tox 可用於自動化測試和建構,而 Poetry 提供更全面的專案管理功能,包含依賴管理、虛擬環境管理、套件構建和釋出等。
使用 pyproject.toml 管理 Python 專案的版本與依賴
在現代 Python 專案開發中,pyproject.toml 檔案已成為管理專案依賴、版本和建置設定的標準方式。本文將探討如何利用 pyproject.toml 自動化版本管理、宣告專案依賴,以及建立可分發的套件。
自動化版本管理
為了保持專案版本的自動更新,我們可以撰寫指令碼讀取並修改 pyproject.toml 中的版本資訊。以下是一個實用的版本更新指令碼範例:
import pathlib
import zoneinfo
import datetime
import tomlkit
# 取得當前 UTC 時間
now = datetime.datetime.now(tz=zoneinfo.ZoneInfo("UTC"))
prefix = f"{now.year}.{now.month}."
# 載入 pyproject.toml
pyproject = pathlib.Path("pyproject.toml")
data = tomlkit.loads(pyproject.read_text())
# 取得當前版本
current = data["project"].get("version", "")
# 計算新的版本序號
if current.startswith(prefix):
serial = int(current.split(".")[-1]) + 1
else:
serial = 0
# 更新版本資訊
version = prefix + str(serial)
data["project"]["version"] = version
# 將更新寫回 pyproject.toml
pyproject.write_text(tomlkit.dumps(data))
內容解密:
- 時間與版本字首:使用
datetime和zoneinfo取得當前 UTC 時間,並生成版本字首(如2023.10.)。 - 載入與解析
pyproject.toml:使用tomlkit讀取並解析pyproject.toml檔案內容。 - 版本邏輯判斷:檢查當前版本是否符合新的字首,若符合則遞增序號,否則重置為 0。
- 寫回更新:將新的版本資訊寫回
pyproject.toml。
管理專案依賴
在 pyproject.toml 中宣告專案依賴是確保專案可重複建置的關鍵。建議使用寬鬆的依賴版本宣告,例如:
[project]
dependencies = [
"Twisted>=17.5",
]
內容解密:
- 寬鬆依賴:使用
>=指定最低相容版本,避免過於嚴格的版本鎖定。 - 避免精確版本鎖定:除非必要(如使用私有 API),否則應避免使用
==鎖定特定版本。
使用 importlib.metadata 管理版本暴露
為了避免多處維護版本資訊,我們可以使用 importlib.metadata 在套件初始化檔案中動態取得版本:
# example_package/__init__.py
from importlib import metadata
__version__ = metadata.distribution("example_package").version
del metadata # 清理頂層名稱空間
內容解密:
- 動態取得版本:透過
importlib.metadata.distribution方法動態讀取套件的版本資訊。 - 名稱空間清理:刪除不再需要的參照,保持名稱空間整潔。
建置與釋出套件
使用 python -m build 可以輕鬆建立 wheel 或原始碼發行版:
# 建立 wheel 發行版
python -m build --wheel
# 建立原始碼發行版
python -m build --sdist
內容解密:
python -m build:使用build工具建立可分發的套件格式。--wheel與--sdist:分別建立 wheel 和原始碼發行版,以滿足不同使用者的安裝需求。
建置包含原生擴充的套件
當專案包含原生擴充(如 Cython 程式碼)時,需要額外的設定:
[build-system]
requires = ["setuptools", "cython"]
# setup.py
import setuptools
from Cython import Build
setuptools.setup(
ext_modules=Build.cythonize("binary_module.pyx"),
)
內容解密:
build-system設定:在pyproject.toml中宣告建置原生擴充所需的工具(如 Cython)。Cython.Build.cythonize:將.pyx檔案轉譯為可建置的擴充模組。
建構與管理二進位套件
建構二進位套件是釋出 Python 專案的重要步驟。透過使用 python -m build --wheel 指令,可以建立一個二進位 wheel 檔案,名稱類別似於 binary_example-1.0-cp39-cp39-linux_x86_64.whl。這個檔案的名稱取決於平台、架構和 Python 版本。
安裝與使用二進位 wheel
安裝二進位 wheel 後,可以直接使用其中的模組。例如:
$ pip install dist/binary_example*.whl
$ python -c 'import binary_module; print(binary_module.add(1, 2))'
3
這個範例展示了二進位套件的基本機制,雖然實際的二進位套件通常更複雜,可能涉及演算法最佳化或原生程式函式庫的封裝。
manylinux Wheels
二進位 wheel 不是純粹的 Python wheel,因為其中至少包含一個原生程式碼檔案。在 Linux 系統上,這個檔案是一個共用函式庫(.so 檔案)。這個共用函式庫會連結到其他函式庫,通常包括標準 C 函式庫。
Self-Contained Wheels
auditwheel 工具可以將二進位 wheel 封裝成自包含的形式,使其不依賴外部函式庫。首先,建立一個正常的二進位 wheel,然後執行:
$ auditwheel repair --plat linux_x86_64 dist/*.whl
這會在 wheelhouse 子目錄中建立一個自包含的 wheel 檔案。
Portable Wheels
使用 auditwheel 時,--plat 旗標指定了平台標籤。如果使用 linux_<cpu architecture>,則 wheel 檔案不保證與特定版本的 GNU C Library 相容。為了使 wheel 檔案可上傳到 PyPI,需要使用正確的平台標籤,例如 manylinux_2_24 或 manylinux_2_27。
使用 manylinux Containers
為了簡化建構過程,可以使用官方的 manylinux container images。這些映像檔包含了所有必要的工具和 Python 版本。例如:
$ docker run --rm -it quay/pypa/manylinux_2_24
# apt-get update
# apt-get install -y <dependencies>
這樣可以確保建構環境的一致性和正確性。
安裝 manylinux Wheels
預設情況下,pip 會使用相容的 manylinux wheel。如果需要強制使用預先建構的 wheel,可以使用 --only-binary :all: 選項。
tox 自動化測試與建構
tox 是一個自動化管理虛擬環境的工具,用於測試和建構。它可以確保測試和建構在定義好的環境中執行,並且可以快取環境以減少重複工作。tox 的設定通常放在測試環境中。
使用 tox 的優勢
- 自動化管理虛擬環境
- 確保測試和建構的一致性
- 快取環境以減少重複工作
tox 的基本使用
tox 的設定檔通常是 tox.ini,在其中定義不同的測試環境和建構環境。例如:
[tox]
envlist = py37,py38,py39
[testenv]
commands = python -m unittest discover -s tests
這個範例定義了三個測試環境,分別對應 Python 3.7、3.8 和 3.9 版本,並且指定了測試指令。
詳細解說:
[tox]區塊定義了 tox 的基本設定,例如envlist指定了要建立的虛擬環境列表。[testenv]區塊定義了測試環境的設定,例如commands指定了要執行的測試指令。
tox 的使用可以簡化測試和建構的流程,並且可以確保環境的一致性。透過使用 tox,可以更輕鬆地管理和維護專案的測試和建構流程。
tox 組態
tox 是一個用於自動化測試的工具,通常安裝在虛擬環境中。由於 tox 會為測試建立臨時的虛擬環境,因此 tox 所安裝的虛擬環境可以被多個專案共用。
安裝 tox
建立一個專用的虛擬環境來安裝 tox 是一個常見的做法。可以使用以下命令來建立虛擬環境並安裝 tox:
$ python -m venv ~/.venvs/tox
$ ~/.venvs/tox/bin/python -m pip install tox
$ alias tox=~/.venvs/tox/bin/tox
內容解密:
python -m venv ~/.venvs/tox:建立一個名為tox的虛擬環境,存放在~/.venvs/目錄下。~/.venvs/tox/bin/python -m pip install tox:在剛建立的虛擬環境中安裝 tox。alias tox=~/.venvs/tox/bin/tox:設定一個別名tox,直接指向虛擬環境中的 tox 可執行檔。
tox 組態檔案
tox 使用 ini 格式的組態檔案,通常命名為 tox.ini。組態檔案中的每個 section 對應一個測試環境。
[testenv:some-name]
...
內容解密:
[testenv:some-name]:定義一個名為some-name的測試環境。- 如果環境名稱包含
pyNM(例如py36),tox 會預設使用 CPython 的 N.M 版本(此例中為 3.6)作為 Python 直譯器。
簡單的 tox 組態示例
單一環境組態
[tox]
envlist = py39
[testenv]
deps =
flake8
commands =
flake8 useful
內容解密:
[tox]:全域性組態段,envlist = py39指定了要使用的測試環境。[testenv]:測試環境組態段。deps = flake8:指定了要在測試環境中安裝的依賴包。commands = flake8 useful:指定了要在測試環境中執行的命令。
多環境組態
[tox]
envlist = py39,py38
[testenv]
deps =
pytest
hypothesis
pyhamcrest
commands =
pytest useful
內容解密:
envlist = py39,py38:指定了兩個測試環境,分別使用 Python 3.9 和 Python 3.8。- 共用相同的測試環境組態,包括依賴包和執行命令。
複雜的多環境組態
[tox]
envlist = {py38,py39}-{unit,func},py39-wheel,docs
[testenv]
deps =
{py38,py39}-unit: coverage
{py38,py39}-{func,unit}: twisted
{py38,py39}-{func,unit}: ncolony
commands =
{py38,py39}-unit: python -Wall -Wignore::DeprecationWarning -m coverage run -m twisted.trial --temp-directory build/_trial_temp {posargs:ncolony}
{py38,py39}-unit: coverage report --include ncolony* --omit */tests/*,*/interfaces*,*/_version* --show-missing --fail-under=100
{py38,py39}-func: python -Werror -Wignore::DeprecationWarning -Wignore::ImportWarning -m ncolony tests.functional_test
內容解密:
envlist = {py38,py39}-{unit,func},py39-wheel,docs:定義了一個矩陣式的測試環境,包括多個不同組態的測試環境。- 使用條件式的依賴包和執行命令組態,以適應不同的測試環境需求。
獨立的測試環境組態
[testenv:py39-wheel]
skip_install = True
deps =
build
commands =
python -c 'import os, sys; os.makedirs(sys.argv[1])' {envtmpdir}/dist
python -m build --outdir {envtmpdir}/dist --no-isolation
內容解密:
[testenv:py39-wheel]:定義了一個名為py39-wheel的獨立測試環境,用於構建 wheel 包。skip_install = True:跳過安裝步驟。- 使用特定的依賴包和執行命令來構建 wheel 包。
檔案構建測試環境
[testenv:docs]
changedir = docs
deps =
sphinx
commands =
sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
basepython = python3.9
內容解密:
[testenv:docs]:定義了一個名為docs的測試環境,用於構建檔案。changedir = docs:切換到docs目錄。- 使用 Sphinx 工具來構建 HTML 檔案。
管理 Python 專案依賴的工具與技術
在 Python 開發過程中,管理專案依賴是一項重要任務。正確管理依賴可以確保專案在不同環境中的一致性、可重複性和穩定性。本篇文章將介紹幾種流行的工具和技術,用於管理 Python 專案的依賴。
2.7 Pip Tools
Pip Tools 是一個 PyPI 套件,提供了一個專門的命令來凍結依賴。pip-compile 命令接受一個寬鬆的 requirements.in 檔案作為輸入,並生成一個具有嚴格版本的 requirements.txt 檔案。
基本用法
執行 pip-compile 命令的基本語法如下:
$ pip-compile < requirements.in > requirements.txt
通常,寬鬆的依賴已經在 setup.cfg 檔案中定義。例如,一個 Web 應用程式的 setup.cfg 可能包含對 gunicorn 的依賴和對 pytest 的測試依賴:
[options]
install_requires=
gunicorn
[options.extras_require]
test =
pytest
在這種情況下,pip-compile 可以直接使用 setup.cfg 作為輸入:
$ pip-compile > requirements.txt
生成的 requirements.txt 檔案包含嚴格的依賴版本:
gunicorn==20.1.0
生成測試依賴
可以使用 --extra 引數生成測試依賴的 requirements-test.txt 檔案:
$ pip-compile --extra test > requirements-test.txt
這將生成包含測試依賴的嚴格版本檔案:
attrs==21.4.0
gunicorn==20.1.0
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.0.6
pytest==6.2.5
toml==0.10.2
注意事項
- Pip Tools 盡量複製 pip 的解析演算法,但在某些邊緣情況下可能會有所不同。
- 為了提高解析的準確性,可以為某些依賴新增特定的版本約束,例如
>=。 - 即使是簡單的 Python 程式,也可能有數十個依賴。因此,檢查生成的
requirements.txt檔案到版本控制系統中是非常重要的。
2.8 Poetry
Poetry 是一個包管理和依賴管理系統,提供了一整套工具來管理 Python 開發過程中的各個方面,包括依賴管理、虛擬環境建立、套件構建和發布,以及 Python 應用程式安裝。
安裝 Poetry
有多種方式可以安裝 Poetry。可以使用 get-poetry.py 指令碼進行安裝:
$ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
或者下載指令碼後手動執行。
也可以在虛擬環境中使用 pip 安裝 Poetry:
$ pip install poetry
安裝完成後,可以使用 poetry self update 命令更新 Poetry 到最新版本。
使用 Poetry 建立新專案
Poetry 提供了一個簡單的方式來建立新的專案結構:
$ poetry new simple_app
這將建立一個名為 simple_app 的目錄,其中包含基本的 Poetry 專案結構。
#### 內容解密:
Poetry 提供了一種簡便的方法來建立和管理 Python 專案。透過使用 Poetry,開發者可以輕鬆管理專案依賴、建立虛擬環境、構建和發布套件,以及安裝 Python 應用程式。Poetry 的安裝和使用都非常簡單,適合用於新的和現有的專案中。