setup.py硬编码版本号易导致版本与git标签脱节,引发包安装版本错误及ci构建不一致;应删去version字段,改用setuptools-scm动态提取git标签生成pep 440兼容版本,如1.2.0.dev3+gabc123d。

为什么 setup.py 里写死版本号会出问题?
手动维护 __version__ 或 setup.py 中的 version="1.2.3",很容易和 Git 标签脱节。比如打了 v2.0.0 标签,但代码里还是 "1.9.9",pip install . 装出来的包版本就错。更麻烦的是,CI 构建时如果依赖本地文件版本,不同分支、不同提交构建结果可能不一致。
如何让 setuptools_scm 从 Git 提取真实版本?
核心是删掉 setup.py 或 pyproject.toml 里的硬编码 version 字段,改用 setuptools_scm 动态生成。它默认读取最近 tag(如 v1.2.0),再根据距该 tag 的提交数、是否 dirty 等信息拼出类似 1.2.0.dev3+gabc123d 的开发版版本。
- 确保项目根目录有 Git 仓库,且已打过至少一个符合语义化格式的 tag(如
v1.0.0、1.2.0) - 在
pyproject.toml中启用:[build-system] requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"] build-backend = "setuptools.build_meta" <p>[project]</p><h1>删除 version = "x.y.z" 这一行</h1><p>dynamic = ["version"]</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2506" title="Python 3.14.2"><img src="https://img.php.cn/upload/manual/001/221/864/6a696c31dfa4a111.webp" alt="Python 3.14.2" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/gongju/2506" title="Python 3.14.2" class="overflowclass">Python 3.14.2</a> <p class="overflowclass">Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。</p> </div> <a rel="nofollow" href="/xiazai/gongju/2506" title="Python 3.14.2" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><p>[project.scm] write_to = "src/mypackage/_version.py"</p>
- 如果不用
pyproject.toml,而用setup.py,则需加use_scm_version=True参数,并确保setup.py同级有pyproject.toml或setup.cfg声明依赖
setuptools_scm 版本格式怎么控制?
默认格式对 CI 和 PyPI 友好,但有时需要微调:比如去掉 dev 分支标识、强制使用 PEP 440 兼容格式、或忽略 dirty 状态。这些靠 pyproject.toml 中的 [tool.setuptools_scm] 配置。
-
version_scheme = "post-release":把1.2.0.dev3变成1.2.0.post3,适合发布候选 -
local_scheme = "no-local":禁用+gabc123d这类本地哈希后缀,只保留主版本 -
fallback_version = "0.1.0":当不在 Git 仓库中(如 tarball 解压后)时的兜底版本 - 若 tag 是
1.2.0-rc1,默认会解析为1.2.0rc1;想支持破折号分隔符,需加git_describe_command = "git describe --dirty --tags --long --match 'v*'"
常见错误:安装时报 LookupError: setuptools-scm was unable to detect version
这通常不是配置错,而是环境缺失关键信息:
- 当前目录不在 Git 工作区(
.git文件夹丢失或路径不对) - Git 仓库没提交过任何东西(空仓库无 commit,自然无 tag)
- 打了 tag 但没 push 到远程,而 CI 拉的是 shallow clone(需在 CI 中加
git fetch --prune --unshallow或--depth=0) - 用了
submodule,但主项目没初始化子模块,导致git describe失败 - Windows 上路径含中文或空格,某些旧版
setuptools_scm会解析失败,升级到 8.0+ 可缓解
调试时直接运行 python -m setuptools_scm,它会模拟构建过程并打印详细原因。
真正难搞的是混合工作流:比如主干用 v1.2.0,但某特性分支想基于 v1.2.0-rc2 打 patch,这时 tag 命名规则和 version_scheme 必须对齐,否则生成的版本号既不能排序也无法被 pip 正确比较。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










