14 打包、发布与命令行工具
大约 3 分钟编程语言PythonpackagingCLIwheel
14 打包、发布与命令行工具
一个可交付工具应能在干净环境安装、声明入口、固定依赖范围、输出版本,并生成可验证的 wheel/sdist。
1. 最小项目
ops-inspector/
├── pyproject.toml
├── README.md
├── src/
│ └── ops_inspector/
│ ├── __init__.py
│ ├── __main__.py
│ └── cli.py
└── tests/
└── test_cli.pypyproject.toml:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "ops-inspector"
version = "0.1.0"
description = "Small operations inspection CLI"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "Proprietary" }
authors = [
{ name = "SRE Team" },
]
dependencies = []
[project.optional-dependencies]
dev = [
"pytest>=8",
"mypy>=1.10",
"ruff>=0.8",
]
[project.scripts]
ops-inspector = "ops_inspector.cli:main"
[tool.hatch.build.targets.wheel]
packages = ["src/ops_inspector"]构建后端可以替换,但字段含义保持一致。
2. CLI 入口
# src/ops_inspector/cli.py
import argparse
from collections.abc import Sequence
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="ops-inspector")
parser.add_argument("--version", action="version", version="ops-inspector 0.1.0")
parser.add_argument("--host", required=True)
parser.add_argument("--timeout", type=float, default=5.0)
return parser
def main(argv: Sequence[str] | None = None) -> int:
args = build_parser().parse_args(argv)
print(f"checking {args.host} timeout={args.timeout}")
return 0main(argv) 接收显式参数便于测试。命令行只负责解析和组合依赖,核心逻辑放到可独立测试的函数或模块。
3. __main__.py
from ops_inspector.cli import main
if __name__ == "__main__":
raise SystemExit(main())安装后可以使用两个入口:
ops-inspector --host db-01
python -m ops_inspector --host db-014. 本地构建
python -m pip install --upgrade build
python -m build输出通常位于 dist/:
dist/
├── ops_inspector-0.1.0-py3-none-any.whl
└── ops_inspector-0.1.0.tar.gz在干净虚拟环境中验证:
python -m venv .venv-build-test
.venv-build-test/bin/python -m pip install dist/ops_inspector-0.1.0-py3-none-any.whl
.venv-build-test/bin/ops-inspector --helpWindows 将 .venv-build-test/bin/ 替换为 .venv-build-test\Scripts\。
5. 版本策略
推荐语义化版本:
MAJOR:不兼容变更。MINOR:向后兼容的新功能。PATCH:向后兼容的缺陷修复。
包版本应满足:
- 源码中可查询。
- wheel 元数据和 CLI
--version一致。 - Git tag、构建产物和发布记录可追溯。
- 不手工修改已发布版本。
不要把 0.0.0 或构建时间随意用作长期版本。
6. 发布前检查
python -m ruff check .
python -m mypy src
python -m pytest
python -m build还应检查:
- wheel 中是否意外包含测试数据、密钥或本机路径。
- README 能否正确渲染。
- license、作者和项目 URL 是否准确。
- 依赖范围是否经过干净环境验证。
- 是否在受控内部索引发布并保留审计记录。
上传工具与仓库策略应由组织决定。无论使用 twine、内部制品库还是平台构建,都应使用短期凭据和可重复流水线。
7. 私有包与内网发行
内网发布需要:
- 受控包索引和 TLS。
- 项目级发布权限和令牌轮换。
- 构建产物哈希与供应链扫描。
- Python 版本、平台和 ABI 标签说明。
- 回滚策略和历史版本保留。
若项目只在本仓库运行,不必公开上传。可以先构建 wheel 作为受控制品,再通过镜像或流水线分发。
8. 版本兼容
requires-python = ">=3.11"
dependencies = [
"PyYAML>=6,<7",
]requires-python 与依赖声明不是装饰:
- 低版本解释器会阻止安装。
- 上限能防止未验证的大版本自动进入。
- CI 应至少覆盖最低版本和一个较新版本。
- 条件依赖使用环境标记时,要测试对应平台。
