版本号必须写在包的顶层 __init__.py 或专用 _version.py 中,且严格符合 PEP 440 格式,确保运行时、构建时和依赖解析一致。

<strong>version</strong> 必须出现在包的顶层 <strong>init</strong>.py 或专用 _version.py 中,且格式要严格符合 PEP 440 —— 否则 pip install、twine upload 和依赖解析都会出问题。
版本号必须写在哪儿?不是随便放个变量就行
Python 包的版本号不能只存在文档或 README 里,运行时和构建时都要能被可靠读取。最稳妥的位置只有两个:
-
myproject/__init__.py:直接定义__version__ = "1.2.3",适合小项目;导入包时立即可用(import myproject; myproject.__version__) -
myproject/_version.py:单独文件,只含版本定义和可选的version_info元组,避免__init__.py因导入副作用导致循环依赖或提前失败
注意:setup.py 或 setup.cfg 里的 version 字段已过时;pyproject.toml 中的 [project].version 是构建时读取的“权威源”,但它必须和运行时实际暴露的 __version__ 值一致,否则用户 import 后看到的版本和 pip show 显示的不一致,会引发信任问题。
PEP 440 格式不是“差不多就行”,错一位就上传失败
常见错误是写成 "v1.2.3"、"1.2.3-beta" 或 "1.2.3+git" —— 这些都不合法。正确格式必须满足:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 主次修订号全为数字:
"1.2.3"✔️,"1.2.x"❌ - 预发布后缀用
a/b/rc:"1.2.3a1"✔️,"1.2.3-alpha"❌ - 本地版本用
+号分隔,但仅限构建时临时标记:"1.2.3+local"✔️(PyPI 接受但忽略+后内容),"1.2.3-dev"❌(未加+就是非法预发布) - 构建工具如
build或poetry会在打包前校验该字符串,不合规直接报错:InvalidVersion: "1.2.3-beta" is not a valid PEP 440 version
动态版本号(如 git 描述)要慎用,别让 CI 卡在 git describe 上
想自动从 git tag 生成版本(比如 git describe --tags --always 输出 1.2.3-5-gabc123),可以,但得绕过两个坑:
- 本地开发时没打 tag,
git describe直接失败 → 改用git rev-parse --short HEAD作 fallback,并加+dirty标记未提交修改 - CI 环境(如 GitHub Actions)默认只 fetch 最新 commit,不带 tag → 在 workflow 中显式加
fetch-depth: 0或git fetch --prune --unshallow - 最终拼出的字符串仍需通过
packaging.version.Version校验,不能直接塞进__version__;推荐用setuptools_scm,它内置了这些逻辑,且与pyproject.toml集成简单
多语言混合项目(如含 C++ 扩展)必须双侧同步
像 pybind11 这类项目,C++ 头文件里的宏(PYBIND11_VERSION_MAJOR)和 Python 的 __version__ 必须完全一致。构建 wheel 时,setup.py 会读取两者并比对 —— 不一致就中断,报错信息类似:
ValueError: C++ PYBIND11_VERSION_* macros (2.13.1) do not match Python __version__ ('2.13.0')这意味着你改了 Python 侧版本却忘了更新 C++ 宏,或者反过来。自动化手段有限,最靠谱的做法是:
- 把三处版本来源(C++ 头、
_version.py、pyproject.toml)列在 README 的“发布检查清单”里 - 加一个 pre-commit hook 或 CI step,用正则提取所有位置的版本字符串并断言相等
- 别依赖脚本自动生成 —— 自动生成容易漏掉某一处,而人工核对三行文本反而更可靠
版本号看着只是几个数字,但它是整个分发链路的锚点:用户靠它判断兼容性,工具靠它解析依赖,CI 靠它触发发布流程。写错位置、格式越界、多端不同步,任何一个环节松动,下游就会收到“安装成功但 import 失败”或“pip show 版本和代码里 print 出来的不一样”这类让人半夜爬起来查的日志。

















