告别setup.py!用pyproject.toml统一管理你的Python项目依赖与工具链(附完整配置模板)

张开发
2026/4/12 7:21:14 15 分钟阅读

分享文章

告别setup.py!用pyproject.toml统一管理你的Python项目依赖与工具链(附完整配置模板)
告别setup.py用pyproject.toml统一管理你的Python项目依赖与工具链附完整配置模板你是否曾在深夜被一个老旧的Python项目折磨得焦头烂额setup.py、requirements.txt、pytest.ini、.flake8...这些散落在项目根目录下的配置文件就像是一堆杂乱无章的拼图碎片每次修改都让人提心吊胆。作为一名资深Python开发者我深知这种配置分散带来的痛苦——它让项目维护变得异常艰难也让新成员上手成本陡增。好消息是Python社区已经为我们提供了完美的解决方案pyproject.toml。这个现代化的配置文件可以替代所有传统配置让你的项目从此告别配置混乱。本文将带你深入理解如何用这一个文件统一管理项目的构建、依赖、测试、代码风格检查等所有配置并提供可直接复用的模板配置。1. 为什么Python项目需要配置革命1.1 传统配置方式的痛点剖析让我们先看一个典型Python项目的配置文件分布old_project/ ├── setup.py # 构建配置可能包含危险的可执行代码 ├── setup.cfg # 声明式构建配置INI格式 ├── requirements.txt # 生产环境依赖 ├── dev-requirements.txt # 开发环境依赖 ├── MANIFEST.in # 打包包含的非Python文件 ├── pytest.ini # 测试配置 ├── .flake8 # 代码风格检查 ├── .isort.cfg # import排序配置 └── mypy.ini # 类型检查配置这种分散配置方式存在五大致命缺陷维护噩梦修改一个依赖版本需要在多个文件中同步更新格式混乱Python脚本、INI、纯文本等多种格式混杂安全隐患setup.py作为可执行脚本可能包含恶意代码工具兼容性差不同工具使用不同配置文件难以统一管理新人上手困难需要理解每个文件的用途才能开始开发1.2 pyproject.toml的四大优势对比传统方式pyproject.toml带来了革命性的改进特性传统方式pyproject.toml配置集中度分散在多个文件单一文件统一管理配置格式混合格式Python/INI/TXT标准TOML格式安全性setup.py可执行任意代码纯声明式配置工具支持各工具使用不同配置现代工具原生支持TOMLToms Obvious Minimal Language是一种比JSON更易读的配置文件格式它支持注释、嵌套结构和丰富的数据类型非常适合人类编写和机器解析。2. pyproject.toml核心结构详解2.1 基础骨架必须包含的构建系统配置每个pyproject.toml都必须以[build-system]开头这是PEP 518定义的标准[build-system] requires [setuptools61.0, wheel] build-backend setuptools.build_metarequires构建项目所需的最小依赖集合build-backend指定实际执行构建的后端工具现代Python项目常用的构建后端对比构建后端特点适用场景setuptools功能全面兼容性好传统项目迁移poetry依赖管理一体化新项目快速搭建flit轻量简单纯Python小包hatch功能丰富复杂项目构建2.2 项目元数据PEP 621标准配置[project]部分是项目的核心元数据遵循PEP 621标准[project] name my-awesome-project version 1.0.0 description 一个改变世界的Python项目 authors [ {name 张三, email zhangsanexample.com}, {name 李四, email lisiexample.com} ] license {text MIT} readme README.md requires-python 3.8 dependencies [ requests2.25.0, numpy1.20.0; python_version3.9 ]关键字段说明name项目名称应使用小写和连字符如my-projectversion推荐使用语义化版本SemVerrequires-python指定支持的Python版本范围dependencies项目运行所需的核心依赖版本约束语法速查表操作符示例说明2.0.0大于等于指定版本3.0.0小于指定版本~~2.1.0兼容版本2.1.0,2.2.02.1.3精确版本*2.1.*通配符匹配2.3 可选依赖开发与测试环境分离使用[project.optional-dependencies]可以定义可选依赖组[project.optional-dependencies] dev [ pytest7.0, pytest-cov3.0, black23.0 ] test [ pytest-mock3.0, factory-boy3.0 ] docs [ sphinx5.0, furo2022.0 ]安装时使用方括号语法指定组别# 安装开发依赖 pip install -e .[dev] # 同时安装文档和测试依赖 pip install -e .[docs,test]3. 实战统一所有工具配置3.1 代码格式化与静态检查现代Python项目通常会使用多种代码质量工具现在可以将它们全部配置在pyproject.toml中[tool.black] line-length 88 target-version [py38, py39, py310] include \.pyi?$ exclude /( \.git | \.venv | build | dist )/ [tool.ruff] line-length 88 select [E, F, W, I, B, C4, UP] ignore [E501] [tool.ruff.isort] known-first-party [my_project] [tool.mypy] python_version 3.8 strict true warn_return_any true warn_unused_configs true工具集成说明Black自动格式化代码无需争论代码风格Ruff用Rust编写的极速linter替代flake8isortmypy静态类型检查提升代码健壮性3.2 测试框架配置统一pytest配置告别pytest.ini[tool.pytest.ini_options] testpaths [tests] python_files test_*.py python_classes Test* python_functions test_* addopts -v --covmy_project --cov-reportterm-missing markers [ slow: 标记为运行缓慢的测试, integration: 集成测试 ]3.3 构建与打包高级配置对于需要自定义打包行为的项目[tool.setuptools] packages [my_project] include-package-data true [tool.setuptools.package-data] my_project [data/*.json, templates/*.html] [tool.setuptools.dynamic] version {attr my_project.__version__} readme {file [README.md, CHANGELOG.md]}4. 完整配置模板与迁移指南4.1 新项目启动模板以下是适用于大多数Python项目的完整模板[build-system] requires [setuptools64.0, wheel] build-backend setuptools.build_meta [project] name project-name version 0.1.0 description 项目简短描述 readme README.md license {text MIT} authors [ {name 开发者姓名, email emailexample.com} ] requires-python 3.8 classifiers [ Development Status :: 3 - Alpha, Intended Audience :: Developers, License :: OSI Approved :: MIT License, Programming Language :: Python :: 3 :: Only, Programming Language :: Python :: 3.8, Programming Language :: Python :: 3.9, Programming Language :: Python :: 3.10, ] dependencies [ requests2.28.0, loguru0.6.0 ] [project.optional-dependencies] dev [ black23.0, ruff0.0.270, mypy1.0.0, pytest7.0, pytest-cov4.0 ] docs [ sphinx6.0, sphinx-rtd-theme1.0 ] [project.urls] Homepage https://github.com/username/project-name Documentation https://github.com/username/project-name#readme [tool.black] line-length 88 target-version [py38] [tool.ruff] line-length 88 select [E, F, W, I, B, C4, UP] ignore [E501] [tool.pytest.ini_options] testpaths [tests] addopts -v --covproject_name --cov-reportterm-missing4.2 从旧项目迁移的五个步骤备份现有配置复制所有现有配置文件到备份目录转换setup.cfg使用ini2toml工具自动转换pip install ini2toml[full] ini2toml setup.cfg pyproject.toml合并requirements将requirements.txt内容转换为[project.dependencies]迁移工具配置将.flake8、.isort.cfg等转换为对应的[tool.*]部分测试验证pip uninstall -y your-package pip install -e . pytest4.3 常见迁移问题解决方案Q如何处理动态版本号[project] dynamic [version] [tool.setuptools.dynamic] version {attr my_package.__version__}Q如何包含非Python文件[tool.setuptools.package-data] my_package [data/*.json, templates/*.html]或者使用MANIFEST.in向后兼容include LICENSE recursive-include my_package/data *.jsonQ自定义构建步骤怎么办对于复杂构建需求可以创建build.py# build.py from setuptools import build_meta as _orig def get_requires_for_build_wheel(config_settingsNone): return _orig.get_requires_for_build_wheel(config_settings) # ...其他构建钩子...然后在pyproject.toml中指定[build-system] build-backend build5. 现代Python项目最佳实践5.1 项目结构推荐采用src布局可以避免常见导入问题modern_project/ ├── src/ │ └── my_package/ │ ├── __init__.py │ └── module.py ├── tests/ ├── pyproject.toml └── README.md优势确保测试的是安装后的包而非源码避免Python从当前目录意外导入更清晰的分离项目代码与测试代码5.2 依赖管理的艺术生产依赖使用宽松约束如1.0.0开发依赖可以使用精确版本如23.1.0可选依赖组按功能分组dev、test、docs等PEP 440版本说明符~1.2.3→1.2.3,1.3.0^1.2.3→1.2.3,2.0.0Poetry风格5.3 持续集成(CI)配置示例.github/workflows/test.yml:name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.8, 3.9, 3.10, 3.11] steps: - uses: actions/checkoutv3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-pythonv4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e .[dev] - name: Run tests run: | pytest5.4 版本发布自动化使用bump2version自动化版本管理安装配置pip install bump2version echo [bumpversion] .bumpversion.cfg配置文件.bumpversion.cfg[bumpversion] current_version 0.1.0 commit True tag True [bumpversion:file:pyproject.toml] search version {current_version} replace version {new_version}使用命令bump2version patch # 0.1.0 → 0.1.1 bump2version minor # 0.1.1 → 0.2.0 bump2version major # 0.2.0 → 1.0.0在实际项目中迁移到pyproject.toml后最直观的感受就是再也不用在多个配置文件中来回切换了。所有工具的行为都可以在一个文件中定义和查看新成员加入时也能快速理解项目配置。

更多文章