Python項目配置文件pyproject.toml的完全指南
前言
如果你剛接觸 Python 項目開發(fā),可能會發(fā)現(xiàn)很多開源項目的根目錄下都有一個 pyproject.toml 文件。它是什么?為什么需要它?本文將從零開始,帶你全面了解這個 Python 生態(tài)中的"配置中樞"。
一、什么是 pyproject.toml?
1.1 定義
pyproject.toml 是 Python 項目的標準化配置文件,采用 TOML (Tom’s Obvious Minimal Language) 格式編寫。它在 2016 年通過 PEP 518 被引入 Python 生態(tài)。
1.2 為什么需要它?
在 pyproject.toml 出現(xiàn)之前,Python 項目的配置非?;靵y:
| 文件名 | 作用 | 問題 |
|---|---|---|
setup.py | 定義包的安裝配置 | 代碼形式,容易出錯 |
setup.cfg | setup.py 的配置版本 | 格式陳舊 |
requirements.txt | 列出依賴 | 無法表達復雜依賴關系 |
MANIFEST.in | 指定打包文件 | 額外文件 |
tox.ini | 測試配置 | 專用文件 |
.flake8 | 代碼檢查配置 | 專用文件 |
問題總結:
- 配置文件分散,難以管理
- 格式不統(tǒng)一(INI、Python 代碼、純文本)
- 缺乏標準化
pyproject.toml 的解決方案:
- ? 一個文件統(tǒng)一管理
- ? 現(xiàn)代化的 TOML 格式
- ? Python 官方標準
二、TOML 格式速成
在深入 pyproject.toml 之前,先了解 TOML 的基本語法:
# 這是注釋
# 鍵值對
name = "my-project"
version = "1.0.0"
# 數(shù)組
dependencies = [
"requests>=2.28.0",
"numpy>=1.20.0"
]
# 表(類似于字典/對象)
[tool.black]
line-length = 88
target-version = ['py38']
# 嵌套表
[project.optional-dependencies]
dev = ["pytest>=7.0", "black>=22.0"]
docs = ["sphinx>=4.0"]
特點:
- 簡潔易讀
- 支持注釋
- 類型明確(字符串、數(shù)字、布爾值、數(shù)組、表)
三、pyproject.toml 的核心結構
一個完整的 pyproject.toml 通常包含以下幾個部分:
[build-system] # 構建系統(tǒng)配置 [project] # 項目元數(shù)據(jù) [project.optional-dependencies] # 可選依賴 [tool.xxx] # 各種工具的配置
讓我們逐一解析。
四、詳細解析各個部分
4.1 [build-system] - 構建系統(tǒng)配置
這是 pyproject.toml 的必需部分,告訴 Python 如何構建你的項目。
[build-system] requires = ["setuptools>=45", "wheel"] build-backend = "setuptools.build_meta"
字段說明:
| 字段 | 說明 | 示例 |
|---|---|---|
requires | 構建項目所需的依賴包 | ["setuptools>=45", "wheel"] |
build-backend | 指定使用哪個構建后端 | setuptools.build_meta |
常見構建后端:
setuptools(傳統(tǒng)方式)
[build-system] requires = ["setuptools>=45", "wheel"] build-backend = "setuptools.build_meta"
poetry(現(xiàn)代化工具)
[build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api"
flit(輕量級)
[build-system] requires = ["flit_core >=3.2,<4"] build-backend = "flit_core.buildapi"
hatchling(新興工具)
[build-system] requires = ["hatchling"] build-backend = "hatchling.build"
4.2 [project] - 項目元數(shù)據(jù)
定義項目的基本信息和依賴關系。
[project]
name = "my-awesome-package"
version = "1.0.0"
description = "一個很棒的 Python 包"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "張三", email = "zhangsan@example.com"}
]
maintainers = [
{name = "李四", email = "lisi@example.com"}
]
keywords = ["web", "api", "framework"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
]
dependencies = [
"requests>=2.28.0,<3.0.0",
"click>=8.0.0",
"pydantic>=2.0.0"
]
[project.urls]
Homepage = "https://github.com/username/my-awesome-package"
Documentation = "https://my-awesome-package.readthedocs.io"
Repository = "https://github.com/username/my-awesome-package"
Changelog = "https://github.com/username/my-awesome-package/blob/main/CHANGELOG.md"
字段詳解:
| 字段 | 必需? | 說明 |
|---|---|---|
name | ? | 包名(發(fā)布到 PyPI 的名稱) |
version | ? | 版本號(遵循 SemVer 規(guī)范) |
description | ? | 簡短描述 |
readme | ? | README 文件路徑 |
requires-python | ? | 支持的 Python 版本 |
license | ? | 開源協(xié)議 |
authors | ? | 作者列表 |
dependencies | ? | 運行時依賴 |
4.3 [project.optional-dependencies] - 可選依賴
用于定義不同場景下的額外依賴,比如開發(fā)、測試、文檔等。
[project.optional-dependencies]
# 開發(fā)依賴
dev = [
"pytest>=7.0",
"pytest-cov>=4.0",
"black>=22.0",
"ruff>=0.1.0",
"mypy>=1.0"
]
# 測試依賴
test = [
"pytest>=7.0",
"pytest-asyncio>=0.21.0",
"httpx>=0.24.0"
]
# 文檔依賴
docs = [
"sphinx>=4.0",
"sphinx-rtd-theme>=1.0"
]
# 所有依賴
all = [
"my-awesome-package[dev,test,docs]"
]
使用方式:
# 安裝基礎依賴 pip install my-awesome-package # 安裝開發(fā)依賴 pip install my-awesome-package[dev] # 安裝多個可選依賴組 pip install my-awesome-package[dev,test] # 安裝所有依賴 pip install my-awesome-package[all]
4.4 [project.scripts] - 命令行入口
定義安裝后可在命令行直接調用的命令。
[project.scripts] my-cli = "my_package.cli:main" my-tool = "my_package.tools:run"
示例場景:
假設你有以下代碼結構:
my_package/ ├── __init__.py └── cli.py
cli.py 內(nèi)容:
def main():
print("Hello from my-cli!")
if __name__ == "__main__":
main()
安裝后,用戶可以直接運行:
my-cli # 輸出:Hello from my-cli!
4.5 [tool.*] - 工具配置
這是 pyproject.toml 最強大的功能之一:集中管理各種開發(fā)工具的配置。
4.5.1 pytest(測試框架)
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--cov=my_package",
"--cov-report=html",
"--cov-report=term-missing",
"-v"
]
4.5.2 black(代碼格式化)
[tool.black] line-length = 88 target-version = ['py38', 'py39', 'py310'] include = '\.pyi?$' extend-exclude = ''' /( # 排除的目錄 \.eggs | \.git | \.venv | build | dist )/ '''
4.5.3 ruff(快速 Linter)
[tool.ruff]
line-length = 88
target-version = "py38"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
]
ignore = [
"E501", # 行過長(由 black 處理)
"B008", # 函數(shù)調用中的參數(shù)默認值
]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # 允許未使用的導入
4.5.4 mypy(類型檢查)
[tool.mypy] python_version = "3.8" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true disallow_incomplete_defs = true check_untyped_defs = true no_implicit_optional = true [[tool.mypy.overrides]] module = "tests.*" disallow_untyped_defs = false
4.5.5 coverage(代碼覆蓋率)
[tool.coverage.run]
source = ["my_package"]
omit = [
"*/tests/*",
"*/__init__.py"
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
]
五、實戰(zhàn)案例:完整的 pyproject.toml
下面是一個生產(chǎn)環(huán)境級別的完整示例:
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "data-processor"
version = "2.3.1"
description = "一個強大的數(shù)據(jù)處理庫"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "數(shù)據(jù)團隊", email = "data@company.com"}
]
keywords = ["data", "processing", "etl", "pipeline"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"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",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"pandas>=2.0.0,<3.0.0",
"numpy>=1.24.0",
"sqlalchemy>=2.0.0",
"pydantic>=2.0.0",
"click>=8.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.4.0",
"pytest-cov>=4.1.0",
"pytest-asyncio>=0.21.0",
"black>=23.0.0",
"ruff>=0.1.0",
"mypy>=1.5.0",
"pre-commit>=3.3.0",
]
postgres = [
"psycopg2-binary>=2.9.0",
]
mysql = [
"pymysql>=1.1.0",
]
all = [
"data-processor[dev,postgres,mysql]"
]
[project.urls]
Homepage = "https://github.com/company/data-processor"
Documentation = "https://data-processor.readthedocs.io"
Repository = "https://github.com/company/data-processor"
Issues = "https://github.com/company/data-processor/issues"
[project.scripts]
data-process = "data_processor.cli:main"
dp = "data_processor.cli:main"
# pytest 配置
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = [
"--cov=data_processor",
"--cov-report=html",
"--cov-report=term-missing:skip-covered",
"-v",
"--strict-markers",
]
markers = [
"slow: 標記慢速測試",
"integration: 集成測試",
"unit: 單元測試",
]
# black 配置
[tool.black]
line-length = 100
target-version = ['py38', 'py39', 'py310', 'py311']
include = '\.pyi?$'
# ruff 配置
[tool.ruff]
line-length = 100
target-version = "py38"
[tool.ruff.lint]
select = ["E", "F", "I", "B", "C4", "UP"]
ignore = ["E501"]
[tool.ruff.lint.isort]
known-first-party = ["data_processor"]
# mypy 配置
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
plugins = ["pydantic.mypy"]
# coverage 配置
[tool.coverage.run]
source = ["data_processor"]
omit = ["*/tests/*", "*/__init__.py"]
[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
六、pyproject.toml vs 傳統(tǒng)方式對比
6.1 依賴管理對比
傳統(tǒng)方式(requirements.txt):
requests>=2.28.0 numpy>=1.20.0 pandas>=2.0.0
問題:
- 無法區(qū)分生產(chǎn)依賴和開發(fā)依賴
- 不支持可選依賴組
- 缺少項目元數(shù)據(jù)
現(xiàn)代方式(pyproject.toml):
[project]
dependencies = [
"requests>=2.28.0",
"numpy>=1.20.0",
"pandas>=2.0.0",
]
[project.optional-dependencies]
dev = ["pytest>=7.0", "black>=22.0"]
優(yōu)勢:
- ? 清晰分類
- ? 靈活安裝
- ? 完整的項目信息
6.2 工具配置對比
傳統(tǒng)方式:
需要多個配置文件:
.flake8pytest.inimypy.initox.ini
現(xiàn)代方式:
一個 pyproject.toml 搞定:
[tool.pytest.ini_options] # pytest 配置 [tool.black] # black 配置 [tool.mypy] # mypy 配置
七、最佳實踐
7.1 版本管理
使用語義化版本(SemVer):
[project] version = "主版本.次版本.修訂號" # 如 "2.3.1"
- 主版本:不兼容的 API 修改
- 次版本:向下兼容的功能新增
- 修訂號:向下兼容的問題修正
7.2 依賴版本約束
dependencies = [
"requests>=2.28.0,<3.0.0", # 推薦:指定范圍
"numpy>=1.20.0", # 可以:僅指定最低版本
"pandas==2.0.0", # 避免:固定版本(除非必要)
]
7.3 項目結構建議
my_project/
├── pyproject.toml # 項目配置
├── README.md # 項目說明
├── LICENSE # 開源協(xié)議
├── .gitignore # Git 忽略文件
├── src/
│ └── my_package/ # 源代碼
│ ├── __init__.py
│ └── ...
└── tests/ # 測試代碼
└── ...
7.4 使用 src 布局
在 pyproject.toml 中配置:
[tool.setuptools]
package-dir = {"" = "src"}
[tool.setuptools.packages.find]
where = ["src"]
優(yōu)勢:
- 避免測試時導入本地未安裝的包
- 確保測試的是安裝后的版本
八、常見問題 FAQ
Q1: pyproject.toml 和 setup.py 可以共存嗎?
答:可以,但不推薦。如果兩者都存在,建議逐步遷移到 pyproject.toml。
Q2: 如何從 setup.py 遷移到 pyproject.toml?
答:
- 創(chuàng)建
pyproject.toml - 將
setup()中的參數(shù)轉換為對應的 TOML 格式 - 測試構建:
pip install -e . - 確認無誤后刪除
setup.py
Q3: Poetry 和 setuptools 選哪個?
答:
- Poetry:現(xiàn)代化,功能豐富,適合新項目
- setuptools:傳統(tǒng),兼容性好,適合維護老項目
Q4: pyproject.toml 中的依賴和 requirements.txt 的關系?
答:
pyproject.toml:定義抽象依賴和項目元數(shù)據(jù)requirements.txt:可選,用于鎖定具體版本
推薦工作流:
# 從 pyproject.toml 生成 requirements.txt pip-compile pyproject.toml -o requirements.txt
九、工具推薦
9.1 Poetry
一站式項目管理工具,自動生成 pyproject.toml。
# 安裝 pip install poetry # 初始化項目 poetry init # 添加依賴 poetry add requests # 安裝依賴 poetry install
9.2 Flit
輕量級打包工具,配置簡單。
# 安裝 pip install flit # 發(fā)布到 PyPI flit publish
9.3 Hatch
現(xiàn)代化項目管理工具。
# 安裝 pip install hatch # 創(chuàng)建項目 hatch new my-project
十、總結
pyproject.toml 是 Python 項目管理的未來趨勢,它帶來了:
? 統(tǒng)一配置:一個文件管理所有配置
? 標準化:Python 官方推薦的標準
? 現(xiàn)代化:更清晰的語法和更強大的功能
? 工具支持:主流工具都已支持
學習路徑建議
- 初級:理解基本結構,能讀懂常見配置
- 中級:為自己的項目編寫
pyproject.toml - 高級:掌握各種工具配置,優(yōu)化項目管理流程
最后的建議:如果你正在開始一個新的 Python 項目,從第一天就使用 pyproject.toml。它不僅讓你的項目更專業(yè),也為未來的維護和協(xié)作打下良好基礎。
以上就是Python項目配置文件pyproject.toml的完全指南的詳細內(nèi)容,更多關于Python配置文件pyproject.toml的資料請關注腳本之家其它相關文章!
相關文章
使用Python內(nèi)置的模塊與函數(shù)進行不同進制的數(shù)的轉換
這篇文章主要介紹了使用Python內(nèi)置的模塊與函數(shù)進行不同進制的數(shù)的轉換的方法,Python也使得讀取純二進制文件內(nèi)容非常方便,需要的朋友可以參考下2016-03-03
基于Python實現(xiàn)簡易的植物識別小系統(tǒng)
這篇文章主要介紹了利用Python實現(xiàn)一個簡易的植物識別系統(tǒng),文中的示例代碼簡潔易懂,對我們學習Python有一定的幫助,需要的小伙伴可以參考一下2021-12-12
Python3.5 Pandas模塊之DataFrame用法實例分析
這篇文章主要介紹了Python3.5 Pandas模塊之DataFrame用法,結合實例形式詳細分析了Python3.5中Pandas模塊的DataFrame結構創(chuàng)建、讀取、過濾、獲取等相關操作技巧與注意事項,需要的朋友可以參考下2019-04-04
python實現(xiàn)監(jiān)控阿里云賬戶余額功能
這篇文章主要介紹了python實現(xiàn)監(jiān)控阿里云賬戶余額功能,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12
Python通過keyboard庫實現(xiàn)模擬和監(jiān)聽鍵盤
這篇文章主要為大家詳細介紹了Python如何通過keyboard庫實現(xiàn)模擬和監(jiān)聽鍵盤,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下2024-10-10
Windows系統(tǒng)下使用flup搭建Nginx和Python環(huán)境的方法
這篇文章主要介紹了Windows系統(tǒng)下使用flup搭建Nginx和Python環(huán)境的方法,文中使用到了flup這個Python的FastCGI工具,需要的朋友可以參考下2015-12-12
Python利用Selenium實現(xiàn)網(wǎng)站自動簽到功能
小編了解到了python的selenium庫對于自動化測試的初學者很容易上手,今天就借這篇文章給大家介紹Python利用Selenium實現(xiàn)網(wǎng)站自動簽到功能,感興趣的朋友一起看看吧2021-09-09

