是否有一种标准的方法将版本字符串与Python包相关联,以便我可以执行以下操作?
import foo
print(foo.version)
我认为有一些方法可以在没有任何额外硬编码的情况下检索数据,因为minor/major字符串已经在setup.py中指定了。我发现的替代解决方案是在我的foo/__init__.py中导入__version__,然后由setup.py生成__version__.py。
是否有一种标准的方法将版本字符串与Python包相关联,以便我可以执行以下操作?
import foo
print(foo.version)
我认为有一些方法可以在没有任何额外硬编码的情况下检索数据,因为minor/major字符串已经在setup.py中指定了。我发现的替代解决方案是在我的foo/__init__.py中导入__version__,然后由setup.py生成__version__.py。
当前回答
如果您使用CVS(或RCS)并想要快速解决方案,您可以使用:
__version__ = "$Revision: 1.1 $"[11:-2]
__version_info__ = tuple([int(s) for s in __version__.split(".")])
(当然,修订号会被CVS代替)
这为您提供了一个打印友好的版本和版本信息,您可以使用它来检查您正在导入的模块至少具有预期的版本:
import my_module
assert my_module.__version_info__ >= (1, 1)
其他回答
《绿箭侠》以一种有趣的方式处理它。
现在(2e5031b起)
在箭头/ __init__ . py:
__version__ = 'x.y.z'
在setup . py:
from arrow import __version__
setup(
name='arrow',
version=__version__,
# [...]
)
之前
在箭头/ __init__ . py:
__version__ = 'x.y.z'
VERSION = __version__
在setup . py:
def grep(attrname):
pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
strval, = re.findall(pattern, file_text)
return strval
file_text = read(fpath('arrow/__init__.py'))
setup(
name='arrow',
version=grep('__version__'),
# [...]
)
同样值得注意的是,__version__是一个半std。在python中,__version_info__也是一个元组,在简单的情况下,你可以这样做:
__version__ = '1.2.3'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])
...你可以从文件或其他地方获取__version__字符串。
重写2017 - 05
在写了13年以上的Python代码和管理各种包之后,我得出的结论是,DIY可能不是最好的方法。
我开始使用pbr包来处理包中的版本控制。如果您正在使用git作为您的SCM,这将像魔法一样适合您的工作流,节省您数周的工作时间(您将惊讶于问题的复杂程度)。
截至目前,pbr的月下载量为1200万次,达到这一水平并不需要任何肮脏的手段。这只是一件事——用非常简单的方法解决一个常见的包装问题。
PBR可以承担更多的包维护负担,而且不局限于版本控制,但它不会强迫您采用它的所有好处。
所以为了给你一个关于如何在一次提交中采用策略br的想法,看看切换包装到策略br
您可能会注意到版本根本没有存储在存储库中。PBR确实从Git分支和标记中检测到它。
不需要担心没有git存储库时会发生什么,因为在打包或安装应用程序时,pbr会“编译”并缓存版本,因此不依赖于git的运行时。
旧的解决方案
以下是我迄今为止见过的最好的解决方案,它也解释了为什么:
借“yourpackage / version.py:
# Store the version here so:
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '0.12'
内部yourpackage / __init__ . py:
from .version import __version__
在setup . py:
exec(open('yourpackage/version.py').read())
setup(
...
version=__version__,
...
如果你知道其他更好的方法,请告诉我。
在与__init__.py相同的文件夹中创建一个名为_version.txt的文件,并将version写成一行:
0.8.2
从__init__.py中的_version.txt文件中读取以下信息:
import os
def get_version():
with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), "_version.txt")) as f:
return f.read().strip()
__version__ = get_version()
这里的许多解决方案都忽略了git版本标签,这仍然意味着你必须在多个地方跟踪版本(不好)。我的目标如下:
从git repo中的一个标记中派生所有python版本引用 自动化git标记/推送和setup.py上传步骤,只需一个命令,无需输入。
工作原理:
从make release命令中,找到git repo中最后一个带标记的版本并对其进行递增。标签被推回到原点。 Makefile将版本存储在src/_version.py中,它将被setup.py读取,并包含在发布版中。不要将_version.py检入源代码控制! Setup.py命令从package.__version__中读取新的版本字符串。
细节:
Makefile
# remove optional 'v' and trailing hash "v1.0-N-HASH" -> "v1.0-N"
git_describe_ver = $(shell git describe --tags | sed -E -e 's/^v//' -e 's/(.*)-.*/\1/')
git_tag_ver = $(shell git describe --abbrev=0)
next_patch_ver = $(shell python versionbump.py --patch $(call git_tag_ver))
next_minor_ver = $(shell python versionbump.py --minor $(call git_tag_ver))
next_major_ver = $(shell python versionbump.py --major $(call git_tag_ver))
.PHONY: ${MODULE}/_version.py
${MODULE}/_version.py:
echo '__version__ = "$(call git_describe_ver)"' > $@
.PHONY: release
release: test lint mypy
git tag -a $(call next_patch_ver)
$(MAKE) ${MODULE}/_version.py
python setup.py check sdist upload # (legacy "upload" method)
# twine upload dist/* (preferred method)
git push origin master --tags
发行版目标总是增加第3个版本的数字,但是您可以使用next_minor_ver或next_major_ver来增加其他数字。这些命令依赖于签入repo根目录的versionbump.py脚本
versionbump.py
"""An auto-increment tool for version strings."""
import sys
import unittest
import click
from click.testing import CliRunner # type: ignore
__version__ = '0.1'
MIN_DIGITS = 2
MAX_DIGITS = 3
@click.command()
@click.argument('version')
@click.option('--major', 'bump_idx', flag_value=0, help='Increment major number.')
@click.option('--minor', 'bump_idx', flag_value=1, help='Increment minor number.')
@click.option('--patch', 'bump_idx', flag_value=2, default=True, help='Increment patch number.')
def cli(version: str, bump_idx: int) -> None:
"""Bumps a MAJOR.MINOR.PATCH version string at the specified index location or 'patch' digit. An
optional 'v' prefix is allowed and will be included in the output if found."""
prefix = version[0] if version[0].isalpha() else ''
digits = version.lower().lstrip('v').split('.')
if len(digits) > MAX_DIGITS:
click.secho('ERROR: Too many digits', fg='red', err=True)
sys.exit(1)
digits = (digits + ['0'] * MAX_DIGITS)[:MAX_DIGITS] # Extend total digits to max.
digits[bump_idx] = str(int(digits[bump_idx]) + 1) # Increment the desired digit.
# Zero rightmost digits after bump position.
for i in range(bump_idx + 1, MAX_DIGITS):
digits[i] = '0'
digits = digits[:max(MIN_DIGITS, bump_idx + 1)] # Trim rightmost digits.
click.echo(prefix + '.'.join(digits), nl=False)
if __name__ == '__main__':
cli() # pylint: disable=no-value-for-parameter
这就完成了如何从git处理和增加版本号的繁重工作。
__init__ . py
my_module/_version.py文件被导入到my_module/__init__.py。将您希望随模块一起分发的任何静态安装配置放在这里。
from ._version import __version__
__author__ = ''
__email__ = ''
setup . py
最后一步是从my_module模块中读取版本信息。
from setuptools import setup, find_packages
pkg_vars = {}
with open("{MODULE}/_version.py") as fp:
exec(fp.read(), pkg_vars)
setup(
version=pkg_vars['__version__'],
...
...
)
当然,要让所有这些工作,你必须在你的回购中至少有一个版本标签才能开始。
git tag -a v0.0.1