我正在尝试将我的包从
setup.py
迁移到pyproject.toml
,但我不确定如何以与以前相同的方式进行动态版本控制。目前,当构建用于开发时,我可以使用环境变量传递开发版本。
setup.py
文件看起来与此类似:
import os
from setuptools import setup
import my_package
if __name__ == "__main__":
dev_version = os.environ.get("DEV_VERSION")
version = dev_version if dev_version else f"{my_package.__version__}"
setup(
name="my_package",
version=version,
...
)
使用
pyproject.toml
文件时有没有办法做类似的事情?
在我写这个答案时,关于如何做到这一点的最真正有效的方法是继续写你的
pyproject.toml
,就像你通常会做的那样,但不是有 version = "..."
字段,而是添加 dynamic = ["version"]
。
然后你可以保留一个非常小的
setup.py
文件,它只解析版本的动态值。例如:
# pyproject.toml
[project]
name = "hello-world"
readme = "README.md"
authors = [...]
dependencies = [...]
dynamic = ["version"]
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta:__legacy__"
# The difference between `__legacy__` and the regular `build_meta`
# is that `__legacy__` does the equivalent of
# `sys.path.insert(0, os.path.dirname(__file__))`.
# This allows you to `import` your modules from the `CWD`.
# If you don't like using `__legacy__` you can
# manually add `CWD` to `sys.path` inside your `setup.py`.
# setup.py
import os
from setuptools import setup
import my_package
dev_version = os.environ.get("DEV_VERSION")
setup(
version=dev_version if dev_version else f"{my_package.__version__}"
)
以下是我对这种方法的评论:
Setuptools 允许您将所有静态元数据/配置参数保存在
pyproject.toml
中,同时使用逻辑来计算 setup.py
中的所有动态部分。
Setuptools 文档明确指出:
通过
提供的元数据和配置是对...setup()
中存在的信息的补充。pyproject.toml
但是,请务必记住按照
spec编写
dynamic = [...]
,以允许 setuptools
覆盖可通过 [project]
表指定的任何值。
setup.py
仅作为可通过 python setup.py ...
直接调用的“可运行 CLI 工具”而被弃用。但它仍然是一个完全有效的配置文件,就像 conftest.py
是 pytest
的有效配置文件,noxfile.py
是 nox
的配置文件一样(您通常不调用 python conftest.py
或 python noxfile.py
...)。
这方面的证据之一是由 PEP 621 的一位作者撰写的文章 https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html(该文章在 中建立了
[project]
表) pyproject.toml
)和 setuptools
的维护者。
请注意,我在示例中使用了
build-backend = "setuptools.build_meta:__legacy__"
而不是 build-backend = "setuptools.build_meta"
。发生这种情况是因为 __legacy__
试图模拟运行 python setup.py ...
的“副作用”行为,即将 CWD
添加到 sys.path
。如果您不喜欢这样,您可以在相关导入之前在 sys.path
文件中显式操作 setup.py
。
我想要类似的东西。
不幸的是,toml 不支持环境变量,而且我认为它永远也不想支持。
我尝试了一些自定义构建后端包装器,并筛选了 setuptools(和 setuputils3)层。
我的结论是,没有比编写一个简短的构建脚本来动态编辑 pyproject.toml 文件更简单的解决方案了...
这种事:
#!/bin/bash
#fix version - as toml doesn't support environment vars...
sed -i -e "s/.*version.*/version=\"${DEV_VERSION}\"/g" pyproject.toml
#build python source and binary packages
python3 -m build
对于您的其他情况(非开发),您可以“动态”拉入版本,如其他人在此答案中所述: https://stackoverflow.com/a/74514742/4126317
如果您使用 Git,另一种可能值得考虑用于您的用例的替代方案是使用
setuptools_scm
。它使用您的 git 标签来执行动态版本控制。你的 pyproject.toml 看起来像这样:
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
"version_scheme" = "post-release"
"local_scheme" = "no-local-version"
"write_to" = "mypackage/version.py"
[tool.setuptools.package-dir]
mypackage = "mypackage"
[project]
name = "mypackage"
...
dynamic = ["version"]