Python 打包实战精要:基于 pyproject.toml 的现代分发、CLI 与 PyPI 发布全流程
Python 打包实战精要基于 pyproject.toml 的现代分发、CLI 与 PyPI 发布全流程【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents本文是 python-packaging 技能 的详细模式参考references/details.md的技术解读面向需要将 Python 库、CLI 工具发布到 PyPI 或私有索引的开发者。读完本文你将掌握一套可直接复制的全功能 pyproject.toml配置、动态版本管理、Click/argparse 两种 CLI 注册方式以及从本地构建到 GitHub Actions 自动化发布的完整链路并看到这些模式在当前仓库真实工程如 plugin-eval、yt-design-extractor中的落地形态。文档定位从导航到细节的第三层该技能采用导航 → 快速上手 → 细节参考的三级结构SKILL.md负责给出包结构、构建后端setuptools/hatchling/flit/poetry、PEP 517/518/621/660 等核心概念与最小化示例references/details.md则是详细模式与完整示例Detailed patterns and worked examples的承载文件编号从 Pattern 4 继续向后延伸覆盖完整配置、动态版本、CLI、构建与发布四大板块。当导航层信息不足以支撑实际落地时就应读取该细节文档。本文即以此为骨架展开。Pattern 4一份可直接落地的全功能 pyproject.tomldetails.md给出了从构建系统、项目元数据到工具链配置一应俱全的完整示例。下面按区块逐段拆解其作用与含义。build-system声明构建后端[build-system] requires [setuptools61.0, wheel] build-backend setuptools.build_meta这是 PEP 517/518 所要求的最小声明requires列出构建时需要的隔离依赖此处要求 setuptools 不低于 61.0这是支持 pyproject.toml 元数据的关键版本门槛build-backend指定由setuptools.build_meta负责实际构建。wheel的加入确保能同时产出 wheel 与源码分发包。projectPEP 621 标准元数据[project] name my-awesome-package version 1.0.0 description An awesome Python package readme README.md requires-python 3.8 license {text MIT} authors [ {name Your Name, email youexample.com}, ] maintainers [ {name Maintainer Name, email maintainerexample.com}, ] keywords [example, package, awesome] 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, Programming Language :: Python :: 3.11, Programming Language :: Python :: 3.12, ] dependencies [ requests2.28.0,3.0.0, click8.0.0, pydantic2.0.0, ]要点说明readme支持字符串文件名或 TOML 表指定 content-type 与文本内容license的{text MIT}写法适用于未使用 SPDX 表达式作为license字符串的 setuptools 版本。classifiers是 PyPI 页面筛选与展示的核心元数据务必同时声明License :: OSI Approved :: ...和各Programming Language :: Python :: 3.x条目并保持与requires-python一致。dependencies使用 PEP 508 规范说明符。示例中的requests2.28.0,3.0.0是典型的兼容范围写法既排除过旧版本又用上限挡住未来不兼容的大版本。optional-dependencies特性开关[project.optional-dependencies] dev [ pytest7.0.0, pytest-cov4.0.0, black23.0.0, ruff0.1.0, mypy1.0.0, ] docs [ sphinx5.0.0, sphinx-rtd-theme1.0.0, ] all [ my-awesome-package[dev,docs], ]dev/docs分组可让用户按需安装pip install -e .[dev]all分组通过自引用语法my-awesome-package[dev,docs]一次性聚合全部可选依赖是发布时常用的全家桶入口。这一模式在当前仓库的 plugins/plugin-eval/pyproject.toml 中同样可见——它将llmclaude-agent-sdk、apianthropic、devpytest/ruff/ty分别分组形成依赖分层。urls 与 scripts项目链接与命令行入口[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 Bug Tracker https://github.com/username/my-awesome-package/issues Changelog https://github.com/username/my-awesome-package/blob/main/CHANGELOG.md [project.scripts] my-cli my_package.cli:main awesome-tool my_package.tools:run [project.entry-points.my_package.plugins] plugin1 my_package.plugins:plugin1[project.scripts]会在安装时自动生成可执行命令如my-cli指向my_package.cli:main这是发布 CLI 工具的标准做法[project.entry-points]则允许第三方通过插件发现机制向你的包注册扩展点。当前仓库中 plugin-eval 的 pyproject.toml 即注册了plugin-eval plugin_eval.cli:app这一脚本入口指向 cli.py 中由 typer 创建的应用对象。tool.setuptools包发现与数据文件[tool.setuptools] package-dir { src} zip-safe false [tool.setuptools.packages.find] where [src] include [my_package*] exclude [tests*] [tool.setuptools.package-data] my_package [py.typed, *.pyi, data/*.json]package-dir { src}将包根目录指向src/配合packages.find.where [src]实现 src 布局source layout自动发现include/exclude控制匹配范围。zip-safe false显式声明包不做 zip 导入避免资源文件在 zip 环境下失效。package-data声明随包安装的非 Python 文件py.typed是 PEP 561 类型标记*.pyi是存根文件data/*.json是运行时数据。这些资源在运行时可通过importlib.resources读取见后文高级模式。tool.black / tool.ruff / tool.mypy / tool.pytest / tool.coverage工具链统一配置[tool.black] line-length 100 target-version [py38, py39, py310, py311] include \.pyi?$ [tool.ruff] line-length 100 target-version py38 [tool.ruff.lint] select [E, F, I, N, W, UP] [tool.mypy] python_version 3.8 warn_return_any true warn_unused_configs true disallow_untyped_defs true [tool.pytest.ini_options] testpaths [tests] python_files [test_*.py] addopts -v --covmy_package --cov-reportterm-missing [tool.coverage.run] source [src] omit [*/tests/*] [tool.coverage.report] exclude_lines [ pragma: no cover, def __repr__, raise AssertionError, raise NotImplementedError, ]将格式化、lint、类型检查、测试与覆盖率配置全部收拢进 pyproject.toml实现了单一配置源。当前仓库的做法与此一致plugin-eval 在 pyproject.toml 中配置了[tool.ruff]line-length 100、[tool.ruff.lint]、[tool.ty]与[tool.pytest.ini_options]并且仓库 Makefile 中的lint/test目标均以uv run --extra dev方式在该项目目录内执行确保使用锁定的工具版本。Pattern 5动态版本管理手工维护version 1.0.0容易与 git tag 脱节动态版本是发布自动化的重要一环[build-system] requires [setuptools61.0, setuptools-scm8.0] build-backend setuptools.build_meta [project] name my-package dynamic [version] description Package with dynamic version [tool.setuptools.dynamic] version {attr my_package.__version__} # Or use setuptools-scm for git-based versioning [tool.setuptools_scm] write_to src/my_package/_version.py两种方式二选一属性读取[tool.setuptools.dynamic] version {attr my_package.__version__}从源码读取版本字符串配合# src/my_package/__init__.py __version__ 1.0.0 # Or with setuptools-scm from importlib.metadata import version __version__ version(my-package)git 派生setuptools-scm[tool.setuptools_scm]根据 git tag 自动推导版本并写入_version.py彻底消除手工同步。注意使用dynamic [version]后就不能再在[project]中写静态version。CLI 模式Click 与 argparse 双方案Pattern 6基于 Click 的分组命令# src/my_package/cli.py import click click.group() click.version_option() def cli(): My awesome CLI tool. pass cli.command() click.argument(name) click.option(--greeting, defaultHello, helpGreeting to use) def greet(name: str, greeting: str): Greet someone. click.echo(f{greeting}, {name}!) cli.command() click.option(--count, default1, helpNumber of times to repeat) def repeat(count: int): Repeat a message. for i in range(count): click.echo(fMessage {i 1}) def main(): Entry point for CLI. cli() if __name__ __main__: main()在 pyproject.toml 中注册并测试[project.scripts] my-tool my_package.cli:mainpip install -e . my-tool greet World my-tool greet Alice --greetingHi my-tool repeat --count3click.group()将cli变为命令组子命令通过cli.command()挂载click.version_option()自动提供--version。pip install -e .PEP 660 可编辑安装后即可直接以my-tool命令名调用。Pattern 7基于 argparse 的子命令解析# src/my_package/cli.py import argparse import sys def main(): Main CLI entry point. parser argparse.ArgumentParser( descriptionMy awesome tool, progmy-tool ) parser.add_argument( --version, actionversion, version%(prog)s 1.0.0 ) subparsers parser.add_subparsers(destcommand, helpCommands) # Add subcommand process_parser subparsers.add_parser(process, helpProcess data) process_parser.add_argument(input_file, helpInput file path) process_parser.add_argument( --output, -o, defaultoutput.txt, helpOutput file path ) args parser.parse_args() if args.command process: process_data(args.input_file, args.output) else: parser.print_help() sys.exit(1) def process_data(input_file: str, output_file: str): Process data from input to output. print(fProcessing {input_file} - {output_file}) if __name__ __main__: main()argparse 是标准库方案零第三方依赖。核心手法是add_subparsers(destcommand)声明子命令actionversion提供--version未匹配命令时print_help()并sys.exit(1)。Click 适合命令树复杂、需要丰富交互体验的场景argparse 适合追求最小依赖的纯标准库工具——选择取决于项目约束。构建与发布全流程Pattern 8本地构建# Install build tools pip install build twine # Build distribution python -m build # This creates: # dist/ # my-package-1.0.0.tar.gz (source distribution) # my_package-1.0.0-py3-none-any.whl (wheel) # Check the distribution twine check dist/*python -m build会同时产出 sdist.tar.gz源码分发包与 wheel.whl平台无关的纯 Python 轮子文件名中的py3-none-any即Python 3、无 ABI、任意平台。twine check在发布前校验分发包元数据README 渲染、长描述格式、缺失字段等是否合规。Pattern 9发布到 PyPI# Install publishing tools pip install twine # Test on TestPyPI first twine upload --repository testpypi dist/* # Install from TestPyPI to test pip install --index-url https://test.pypi.org/simple/ my-package # If all good, publish to PyPI twine upload dist/*规范流程是先在 TestPyPI 试上传、试安装确认无误后再正式发布。推荐使用 API token 而非账号密码配置~/.pypirc# Create ~/.pypirc [distutils] index-servers pypi testpypi [pypi] username __token__ password pypi-...your-token... [testpypi] username __token__ password pypi-...your-test-token...Pattern 10GitHub Actions 自动化发布# .github/workflows/publish.yml name: Publish to PyPI on: release: types: [created] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: 3.11 - name: Install dependencies run: | pip install build twine - name: Build package run: python -m build - name: Check package run: twine check dist/* - name: Publish to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: twine upload dist/*工作流以release事件types: [created]为触发器串起checkout → 装构建工具 → 构建 → twine check → 用PYPI_API_TOKEN密钥上传五步。TWINE_USERNAME: __token__配合仓库 Secrets 中的PYPI_API_TOKEN避免了在 CI 日志中泄露凭据。仓库实践印证真实的 pyproject.toml 与 CLI 落地上述模式并非纸上谈兵当前仓库内部就有多个可对照的真实工程plugin-evalpyproject.toml采用hatchling构建后端与packages [src/plugin_eval]的 src 布局[project.scripts]注册plugin-eval plugin_eval.cli:app可选依赖按llm/api/dev分组[tool.pytest.ini_options]与[tool.ruff]集中配置。其 CLI 在 cli.py 中基于 typerClick 的上层封装实现score/certify/init等命令与 details.md 中用[project.scripts]注册入口点的思路完全一致——这正是my-tool my_package.cli:main模式的工程级印证。yt-design-extractorpyproject.toml展示了[project.optional-dependencies]控制重依赖的实践——easyocr分组携带 torch/torchvision约 2GB注释明确提示用uv sync --extra easyocr按需安装[tool.uv] package false声明其为仅依赖工程脚本通过uv run yt-design-extractor.py直接执行而非安装为包。MakefileMakefile仓库约定所有 Python 工具链统一走uvlint/test目标都以uv run --extra dev在 plugin-eval 项目目录内执行保证 ruff/ty 版本与 CI 锁定一致——呼应了 Pattern 4 中工具链配置入 pyproject.toml的价值。延伸阅读高级模式索引details.md末尾将更进阶的主题指向 references/advanced-patterns.md其中覆盖Pattern 11数据文件打包[tool.setuptools.package-data]声明data/*.json、templates/*.html等资源运行时用importlib.resources.files(my_package).joinpath(...)读取Python 3.9Pattern 12命名空间包多仓库共享company/命名空间命名空间目录不写__init__.py各包用include [company.core*]限定Pattern 13C 扩展pyproject.toml 的[tool.setuptools] ext-modules或传统setup.py的ExtensionPattern 14-15语义化版本与 setuptools-scm 的 git 派生版本1.0.1.dev3g1234567Pattern 16-17pip install -e .可编辑安装与隔离环境安装验证Pattern 18-20README 模板、cibuildwheel 多架构 wheel、私有索引--index-url/twine upload --repository-url以及.gitignore、MANIFEST.in模板与发布前的逐项检查清单测试、文档、版本号、CHANGELOG、License、TestPyPI 先行、git tag 等。至此从全功能 pyproject.toml到动态版本 CLI 构建发布 自动化 CI再到仓库内真实工程的对照印证你已经拥有了一套可完整覆盖 Python 库/工具打包发布全生命周期的操作指南。【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考