单一来源的包版本#
待办事项
为除 setuptools 之外的构建后端更新此页面。
有许多技术可以维护项目的版本号的单一真实来源
读取
setup.py
中的文件并获取版本。示例(来自 pip setup.py)import codecs import os.path def read(rel_path): here = os.path.abspath(os.path.dirname(__file__)) with codecs.open(os.path.join(here, rel_path), 'r') as fp: return fp.read() def get_version(rel_path): for line in read(rel_path).splitlines(): if line.startswith('__version__'): delim = '"' if '"' in line else "'" return line.split(delim)[1] else: raise RuntimeError("Unable to find version string.") setup( ... version=get_version("package/__init__.py") ... )
注意
从 setuptools 46.4.0 版本开始,可以通过将以下内容放入项目的
setup.cfg
文件中来实现相同的功能(用“package”替换包的导入名称)[metadata] version = attr: package.__version__
从 setuptools 61.0.0 版本开始,可以在项目的
pyproject.toml
文件中动态指定版本。[project] name = "package" dynamic = ["version"] [tool.setuptools.dynamic] version = {attr = "package.__version__"}
请注意,在
setup.py
的参数中不支持声明性配置指示符,包括attr:
指令。使用外部构建工具,该工具可以管理更新这两个位置,或提供这两个位置都可以使用的 API。
你可以使用的一些工具,不分先后顺序,也不一定完整:bump2version、changes、commitizen、zest.releaser。
将值设置为项目中专用模块(例如
version.py
)中的__version__
全局变量,然后让setup.py
读取该值并将其exec
为变量。version = {} with open("...sample/version.py") as fp: exec(fp.read(), version) # later on we use: version['__version__']
使用此技术的示例:warehouse。
将值放入一个简单的
VERSION
文本文件中,并让setup.py
和项目代码都读取它。with open(os.path.join(mypackage_root_dir, 'VERSION')) as version_file: version = version_file.read().strip()
此技术的优点是它不特定于 Python。任何工具都可以读取版本。
警告
使用此方法时,你必须确保
VERSION
文件包含在你所有的源和二进制分发中(例如,将include VERSION
添加到MANIFEST.in
中)。在
setup.py
中设置值,并让项目代码使用importlib.metadata
API 在运行时获取值。(importlib.metadata
在 Python 3.8 中引入,并作为importlib-metadata
项目提供给较旧版本。)已安装项目的版本可以使用以下 API 获取import sys if sys.version_info >= (3, 8): from importlib import metadata else: import importlib_metadata as metadata assert metadata.version('pip') == '1.2.0'
请注意,
importlib.metadata
API 仅了解安装元数据中的内容,这并不一定是当前导入的代码。如果项目使用此方法在运行时获取其版本,那么需要编辑其
install_requires
值以在 Python 的 3.8 之前的版本上安装importlib-metadata
,如下所示setup( ... install_requires=[ ... 'importlib-metadata >= 1.0 ; python_version < "3.8"', ... ], ... )
importlib.metadata
的较旧(且效率较低)的替代方法是setuptools
提供的pkg_resources
APIimport pkg_resources assert pkg_resources.get_distribution('pip').version == '1.2.0'
如果项目在运行时使用
pkg_resources
获取其自己的版本,则必须将setuptools
添加到项目的install_requires
列表中。使用此技术的示例:setuptools。
将值设置为
__version__
在sample/__init__.py
中,并在setup.py
中导入sample
。import sample setup( ... version=sample.__version__ ... )
警告
虽然此技术很常见,但请注意,如果
sample/__init__.py
从install_requires
依赖项导入包,则此技术将会失败,因为在运行setup.py
时很可能尚未安装这些依赖项。将版本号保留在版本控制系统(Git、Mercurial 等)的标记中,而不是保留在代码中,并使用 setuptools_scm 从那里自动提取版本号。