将版本嵌入python包的标准方法?


264

有没有一种标准的方式可以将版本字符串与python软件包相关联,从而可以执行以下操作?

import foo
print foo.version

我可以想象有某种方法可以检索数据而无需任何额外的硬编码,因为setup.py已经指定了次要/主要字符串。另一种解决方案,我发现是有import __version__我的foo/__init__.py,然后让__version__.py所产生的setup.py


7
仅供参考,有一个非常好的概述:packaging.python.org/en/latest/...
ionelmc

1
可以使用setuptools从元数据中检索已安装软件包的版本,因此在许多情况下,仅放入版本setup.py就足够了。看到这个问题
saaj 2015年

2
仅供参考,基本上有5种常见模式可用于维护版本号的唯一真实来源(在设置和运行时)。
肯尼迪·林

@ionelmc Python的文档为单解决方案列出了7种不同的选项。这是否与“ 单一真理源 ”的概念相矛盾?
Stevoisiak

@StevenVascellaro不确定您要问什么。那里列出了太多方法,因为包装指南不愿被接受。
ionelmc

Answers:


136

不是直接回答您的问题,而是您应该考虑命名它__version__,而不是version

这几乎是一个准标准。标准库中的许多模块都使用__version__,并且在许多第三方模块中也使用了它,因此它是准标准的。

通常,它__version__是一个字符串,但有时它也是一个浮点数或元组。

编辑:正如S.Lott所提到的(谢谢!),PEP 8明确表示:

模块级Dunder名称

模块级“dunders”(即名称具有两个前缘和两个纵下划线),例如__all____author____version__等应被放置在模块文档字符串之后,但在除了从任何导入语句__future__进口。

您还应确保版本号符合PEP 440中描述的格式(PEP 386是该标准的先前版本)。


9
它应该是一个字符串,并具有一个元组版本的version_info
詹姆斯·安提尔

詹姆斯:为什么要__version_info__具体?(这“发明”了您自己的双下划线词。)[当詹姆斯发表评论时,下划线在评论中什么也没做,现在它们表明了重点,所以詹姆斯也确实写__version_info__了。--- ed。]

您可以在packages.python.org/distribute/…上看到有关应该说什么版本的信息。 该页面是有关distribute的,但是版本号的含义已成为事实上的标准。
sienkiew

2
对。这些PEP似乎相互矛盾。好吧,PEP 8说“ if”和“ crud”,因此它并没有真正支持使用VCS关键字扩展。另外,如果您切换到其他VCS,则会丢失修订信息。因此,我建议至少在较大的项目中使用嵌入到单个源文件中的符合PEP 386/440的版本信息。
oefe 2014年

2
您将把该版本放在哪里。考虑到这是可接受的版本,我很乐意在这里看到其他信息。
darkgaze

120

我使用一个_version.py文件作为“一次规范的位置”来存储版本信息:

  1. 它提供了一个__version__属性。

  2. 它提供了标准的元数据版本。因此,它将由pkg_resources解析包元数据的其他工具(EGG-INFO和/或PKG-INFO,PEP 0345)检测到。

  3. 在构建软件包时,它不会导入您的软件包(或其他任何东西),这在某些情况下可能会导致问题。(请参阅下面的评论,这可能会导致什么问题。)

  4. 写下版本号的位置只有一个,因此,当版本号更改时,只有一个地方可以更改版本号,并且版本不一致的可能性较小。

它是这样工作的:存储版本号的“一个规范位置”是一个.py文件,名为“ _version.py”,位于您的Python软件包中,例如myniftyapp/_version.py。该文件是Python模块,但您的setup.py不会导入它!(这会使功能3失效。)相反,setup.py知道此文件的内容非常简单,类似于:

__version__ = "3.6.5"

因此,您的setup.py将使用以下代码打开文件并对其进行解析:

import re
VERSIONFILE="myniftyapp/_version.py"
verstrline = open(VERSIONFILE, "rt").read()
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
mo = re.search(VSRE, verstrline, re.M)
if mo:
    verstr = mo.group(1)
else:
    raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))

然后,您的setup.py将该字符串作为“ version”参数的值传递给setup(),从而满足功能2的要求。

为了满足功能1,您可以让包(在运行时,而不是在安装时!)从_version文件中导入,myniftyapp/__init__.py如下所示:

from _version import __version__

这是我使用多年的这种技术的示例

该示例中的代码稍微复杂一点,但是我在此注释中编写的简化示例应该是完整的实现。

这是导入版本的示例代码

如果您发现此方法有任何问题,请告诉我。


8
您能否描述一下激发#3的问题?Glyph说,这与“ setuptools喜欢假装您的setup.py运行时您的代码不在系统上的任何地方”有关,但是详细信息将有助于说服我和其他人。
伊万·科兹克

2
@Iva现在,该工具应该按照什么顺序执行?它不能(在setuptools的/ PIP / virtualenv中系统今天)甚至不知道什么DEPS 直到它评估您的setup.py。同样,如果它尝试执行完全深度优先并在执行此操作之前先进行所有深度操作,则如果存在圆形深度操作,则将卡住。但是,如果它在安装依赖项之前尝试构建此软件包,则如果从中导入软件包setup.py,则不一定能够导入其dep或其deps的正确版本。
Zooko

3
您能否从“ setup.py” 写入文件“ version.py”而不是对其进行解析?这似乎更简单。
乔纳森·哈特利'02

3
乔纳森·哈特利(Jonathan Hartley):我同意,您的“ setup.py”而不是解析文件,写“ version.py”文件会稍微简单些,但是当您编辑setup.py时,它将打开一个不一致的窗口。具有新版本,但尚未执行setup.py来更新version.py文件。将规范版本保存在单独的小文件中的另一个原因是,它使其他工具(例如读取修订版本状态的工具)可以轻松地编写版本文件。
Zooko

3
类似的方法是execfile("myniftyapp/_version.py")从setup.py内部进行,而不是尝试手动解析版本代码。在stackoverflow.com/a/2073599/647002中建议-讨论可能也有帮助。
medmunds 2013年

97

改写2017-05

经过十多年的编写Python代码和管理各种程序包的经历,我得出的结论是,DIY可能不是最好的方法。

我开始使用pbr软件包来处理软件包中的版本控制。如果您将git用作SCM,它将像魔术一样适合您的工作流程,从而节省了数周的工作(您可能会对问题的复杂程度感到惊讶)。

截至目前,pbr在最常用的python软件包中排名第11,并且达到这一水平还没有任何肮脏的技巧:仅仅是一个:用一种非常简单的方法解决了常见的包装问题。

pbr 可以承担更多的程序包维护负担,不仅限于版本控制,还不强迫您采用其所有优点。

因此,为了让您了解一次提交中采用pbr的外观,请看一下将包装夹到pbr

可能您会发现该版本根本没有存储在存储库中。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__,
    ...

如果您知道另一种似乎更好的方法,请告诉我。


12
嗯不 execfile()在Python 3中不存在,因此最好使用exec(open()。read())。
Christophe Vu-Brugier

4
为什么不在from .version import __version__setup.py中呢?
Aprillion 2015年

4
@Aprillion因为setup.py运行时未加载软件包-试试它,您会收到错误消息(或者至少我做过:
darthbith 2015年

3
指向pbr的链接导致网关错误。
MERose

4
毫无疑问,pbr是一个很好的工具,但是您没有解决这个问题。如何通过bpr访问当前版本或已安装的软件包。
nad2000

29

根据递延的PEP 396(模块版本号),有一种建议的方法。它从原理上描述了要遵循的模块的一个(公认的可选)标准。这是一个片段:

3)当一个模块(或包)包括一个版本号时,该版本应该在__version__属性中可用。

4)对于位于名称空间包中的模块,该模块应包含该__version__属性。命名空间包本身不应包含其自己的__version__属性。

5)__version__属性的值应该是一个字符串。


13
该PEP未被接受/标准化,但被推迟(由于缺乏兴趣)。因此,声明由它指定的“ 有一种标准方法 ” 有点误导。
weaver 2014年

@weaver:天哪!我学到新东西。我不知道这是我需要检查的东西。
Oddthinking 2014年

4
编辑以注意这不是标准。现在,我感到很尴尬,因为我对项目提出了功能要求,要求它们遵循此“标准”。
Oddthinking 2014年

1
也许您应该接手该PEP的标准化工作,因为您似乎有兴趣:)
weaver 2014年

这适用于对单个模块进行版本控制,但是我不确定它是否适用于对整个项目进行版本控制。
Stevoisiak

21

尽管这可能为时已晚,但是对于先前的答案有一个稍微简单的替代方法:

__version_info__ = ('1', '2', '3')
__version__ = '.'.join(__version_info__)

(使用来将版本号的自动递增部分转换为字符串将是非常简单的str()。)

当然,据我所见,人们在使用时通常会使用类似先前提到的版本__version_info__,并将其存储为int元组;但是,我不太明白这样做的意义,因为我怀疑在某些情况下您会出于好奇或自动递增的目的而出于任何目的对版本号的某些部分执行数学运算,例如对版本号进行加减运算(即使如此,int()并且str()可以很容易地使用)。(另一方面,其他人的代码可能期望数字元组而不是字符串元组,从而导致失败。)

当然,这是我自己的观点,我很高兴希望其他人使用数字元组提供输入。


正如shezi提醒我的那样,数字字符串的(词法)比较不一定具有与直接数字比较相同的结果;为此,将需要前导零。因此,最后,将__version_info__(或将要调用的任何形式)存储为整数值的元组将允许更有效的版本比较。


12
不错(+1),但您不是更喜欢数字而不是字符串吗?例如__version_info__ = (1,2,3)
orip

3
当版本号超过9时,字符串比较会变得很危险,因为例如'10'<'2'。
D Coetzee

13
我这样做很好,但略显扭捏地址整数.. __version_info__ = (0, 1, 0) __version__ = '.'.join(map(str, __version_info__))
rh0dium

2
问题__version__ = '.'.join(__version_info__)是,__version_info__ = ('1', '2', 'beta')将成为1.2.beta1.2beta1.2 beta

4
我认为这种方法的问题是在哪里放置声明__version__的代码行。如果它们位于setup.py中,则您的程序无法从其程序包中导入它们。也许这对您来说不是问题,在这种情况下可以。如果它们进入您的程序,则setup.py可以导入它们,但不能导入它们,因为setup.py会在安装过程中在尚未安装程序的依赖项时运行(setup.py用于确定依赖项是什么) 。)因此,Zooko的回答是:手动从产品源文件中解析值,而无需导入产品包
Jonathan Hartley'2

11

这里的许多解决方案都忽略了git版本标记,这仍然意味着您必须在多个位置跟踪版本(错误)。我通过以下目标实现了这一目标:

  • 派生的从标签的所有Python版本引用git回购
  • 使用一个无需输入的命令自动执行git tag/ pushsetup.py upload步骤。

这个怎么运作:

  1. make release命令中,找到并递增git repo中的最后一个标记版本。标签被推回到origin

  2. Makefile存储的版本在src/_version.py那里将被读取setup.py,并且还包含在释放。不要检_version.py入源代码管理!

  3. setup.py命令从中读取新版本字符串package.__version__

细节:

生成文件

# 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

release目标总是递增第三版数字,但可以使用next_minor_vernext_major_ver递增其他数字。这些命令依赖于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

1
确实-整个主题都忘记了VCS在此讨论中非常重要。只是个麻烦:版本增加应该仍然是一个手动过程,因此首选方法是1.手动创建并推送标签2.让VCS工具发现该标签并将其存储在需要的地方(哇-这个SO编辑界面确实让我很沮丧-我不得不对此进行多次编辑,以处理换行符,但仍然无法使用!@#$%^&*)
axd

versionbump.py当我们有一个很棒的python 颠簸软件包时,无需使用。
奥兰

@Oran我看着versionbump。该文档不是很清楚,并且不能很好地处理标记。在我的测试中,它似乎进入了导致其崩溃的状态:subprocess.CalledProcessError:命令'['git','commit','-F','/ var / folders / rl / tjyk4hns7kndnx035p26wg692g_7t8 / T / tmppishngbo'] '返回了非零退出状态
1。– cmcginty

1
为什么不_version.py应该使用版本控制进行跟踪?
Stevoisiak

1
@StevenVascellaro这使发布过程复杂化。现在,您必须确保自己的标签和提交与_version.py中的值匹配。使用单个版本标签可以简化IMO,这意味着您可以直接从github UI创建发行版。
cmcginty

10

我在包目录中使用JSON文件。这符合Zooko的要求。

内部pkg_dir/pkg_info.json

{"version": "0.1.0"}

内部setup.py

from distutils.core import setup
import json

with open('pkg_dir/pkg_info.json') as fp:
    _info = json.load(fp)

setup(
    version=_info['version'],
    ...
    )

内部pkg_dir/__init__.py

import json
from os.path import dirname

with open(dirname(__file__) + '/pkg_info.json') as fp:
    _info = json.load(fp)

__version__ = _info['version']

我还把其他信息放进pkg_info.json,例如作者。我喜欢使用JSON,因为我可以自动管理元数据。


您能否详细说明如何使用json自动执行元数据管理?谢谢!
ryanjdillon '19

6

同样值得一提的是,它还__version__具有半标准性。在python中,__version_info__这是一个元组,在简单的情况下,您可以执行以下操作:

__version__ = '1.2.3'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])

...您可以__version__从文件或任何其他内容中获取字符串。


1
您是否有任何有关此用法来源的参考文献/链接__version_info__
Craig McQueen

3
嗯,这与sys.version到sys.version_info的映射相同。因此:docs.python.org/library/sys.html#sys.version_info
James Antill 2009年

7
在另一个方向(__version_info__ = (1, 2, 3)__version__ = '.'.join(map(str, __version_info__)))进行映射更容易。
Eric O Lebigot

2
@EOL-- __version__ = '.'.join(str(i) for i in __version_info__)稍长,但更pythonic。
ArtOfWarfare

2
我不确定您提出的建议显然是更pythonic的,因为它包含一个真正不需要的虚拟变量,并且其含义有点难以表达(i没有含义,version_num有点长而模棱两可……)。我什至认为map()Python 的存在强烈建议在这里使用它,因为我们在这里需要做的是典型的用例map()(与现有函数一起使用)—我看不到其他许多合理的用法。
Eric O Lebigot

5

似乎没有一种将版本字符串嵌入python包的标准方法。我见过的大多数软件包都使用您的解决方案的某些变体,即eitner

  1. 嵌入版本,setup.pysetup.py生成version.py仅包含版本信息的模块(例如),该模块由您的软件包导入,或者

  2. 相反:将版本信息放入包本身,然后导入在其中设置版本 setup.py


我喜欢您的要求,但是如何从setup.py生成此模块?
sorin 2011年

1
我喜欢选项(1)的想法,它似乎比Zooko从文件中解析版本号的答案更简单。但是您不能确保在开发人员仅克隆您的存储库时就创建了version.py。除非您签入version.py,否则会出现细小的皱纹,您可以在setup.py中更改版本号,然后提交,释放,然后必须(斜线忘记)将更改提交到version.py。
乔纳森·哈特利'02

大概您可以使用类似“”“的open(” mypackage / version.py“,” w“)作为fp来生成模块:fp.write(” __ version__ =='%s'\ n“%(__version__,) )“”“
乔纳森·哈特利

1
我认为如JAB答复的注释中所述,选项2易于在安装期间失败。
乔纳森·哈特利'02

那个怎么样?您将__version__ ='0.0.1'“(当然,版本是字符串)放在软件主程序包的__init__.py”中。然后转到要点2:在安装程序中,从package .__ init__导入__version__作为v,然后将变量v设置为setup.py的版本。然后将mypack作为my导入,打印my .__ version__将打印版本。该版本将仅存储在一个位置(该位置可从整个代码访问),该文件不会导入与该软件相关的任何其他内容。
SeF

5

箭头以一种有趣的方式处理它。

现在(从2e5031b开始

arrow/__init__.py

__version__ = 'x.y.z'

setup.py

from arrow import __version__

setup(
    name='arrow',
    version=__version__,
    # [...]
)

之前

arrow/__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__'),
    # [...]
)

是什么file_text
ely

2
更新的解决方案实际上是有害的。当setup.py运行时,它不一定会从本地文件路径中查看软件包的版本。它可能会还原到以前安装的版本,例如从pip install -e .开发分支上运行或进行测试时还原。setup.py绝对不应依赖于安装过程中的导入软件包来确定部署参数。kes
ely

是的,我不知道为什么箭头决定退回到该解决方案。此外,提交消息显示“使用现代Python标准更新了setup.py ” ...🤷–
Anto,

4

我还看到了另一种风格:

>>> django.VERSION
(1, 1, 0, 'final', 0)

1
是的,我也看到了。顺便说一句,每个答案都采用其他样式,所以现在我不知道什么样式是“标准”。寻找提及的PEP ...
kbec

另一种方式 Mongo的Python客户端使用纯格式,没有下划线。所以这有效;$蟒蛇>>>进口pymongo >>> pymongo.version '2.7'
AnneTheAgile

实施.VERSION并不意味着您不必实施__version__
Acumenus

这是否需要django在项目中实施?
Stevoisiak

3

使用setuptoolspbr

没有管理版本的标准方法,但是管理软件包的标准方法是setuptools

我发现总体上管理版本的最佳解决方案是setuptoolspbr扩展一起使用。现在,这是我管理版本的标准方法。

为完整项目设置项目对于简单项目可能是过大的,但是如果您需要管理版本,则可能处于正确的级别来设置所有内容。这样做还可以使您的软件包在PyPi上发布,因此每个人都可以通过Pip下载和使用它。

PBR将大多数元数据从setup.py工具中移出,并移到一个setup.cfg文件中,然后该文件用作大多数元数据的源,其中可以包括版本。这允许使用pyinstaller所需的类似方法将元数据打包到可执行文件中(如果需要,则可能需要此信息),并将元数据与其他程序包管理/设置脚本分开。您可以直接setup.cfg手动更新版本字符串,并且*.egg-info在构建软件包发行版时会将其拉入文件夹。然后,您的脚本可以使用各种方法从元数据访问版本(这些过程在下面的部分中概述)。

将Git用于VCS / SCM时,此设置甚至更好,因为它将从Git中提取很多元数据,这样您的回购就可以成为某些元数据的主要来源,包括版本,作者,变更日志,专门针对版本,它将基于存储库中的git标签为当前提交创建一个版本字符串。

由于PBR会直接从您的git repo中提取版本,作者,changelog和其他信息,因此setup.cfg每当为您的软件包创建发行版时(使用setup.py),其中的一些元数据就可以省去并自动生成

实时最新版本

setuptools将使用setup.py以下命令实时获取最新信息:

python setup.py --version

这将setup.cfg根据所做的最新提交和存储库中存在的标签,从文件或git存储库中提取最新版本。但是,此命令不会更新发行版中的版本。

更新版本

当您使用setup.pypy setup.py sdist例如)创建分发时,所有当前信息将被提取并存储在分发中。这实际上是运行setup.py --version命令,然后将该版本信息存储package.egg-info在存储分发元数据的一组文件中的文件夹中。

关于更新版本元数据的过程的注释:

如果您不使用pbr从git中提取版本数据,则只需使用新的版本信息直接更新setup.cfg(这很容易,但是请确保这是发布过程的标准部分)。

如果您使用的是git,而无需创建源代码或二进制发行版(使用python setup.py sdistpython setup.py bdist_xxx命令之一),则将git repo信息更新到<mypackage>.egg-info元数据文件夹中的最简单方法就是运行python setup.py install命令。这将运行与从git repo中提取元数据有关的所有PBR功能,并更新本地.egg-info文件夹,为已定义的任何入口点安装脚本可执行文件,以及运行此命令时从输出中看到的其他功能。

请注意,.egg-info通常不会将该文件夹存储在git repo本身的标准Python .gitignore文件中(例如,来自Gitignore.IO),因为可以从您的源中生成该文件夹。如果不包括在内,请确保您具有标准的“发布过程”以在发布之前在本地更新元数据,并且您上载到PyPi.org或以其他方式分发的任何软件包都必须包含此数据以具有正确的版本。如果您希望Git存储库包含此信息,则可以将特定文件排除在忽略范围之外(即添加!*.egg-info/PKG_INFO.gitignore

从脚本访问版本

您可以在包本身的Python脚本中从当前内部版本访问元数据。例如,对于版本,到目前为止,有几种方法可以实现:

## This one is a new built-in as of Python 3.8.0 should become the standard
from importlib-metadata import version

v0 = version("mypackage")
print('v0 {}'.format(v0))

## I don't like this one because the version method is hidden
import pkg_resources  # part of setuptools

v1 = pkg_resources.require("mypackage")[0].version
print('v1 {}'.format(v1))

# Probably best for pre v3.8.0 - the output without .version is just a longer string with
# both the package name, a space, and the version string
import pkg_resources  # part of setuptools

v2 = pkg_resources.get_distribution('mypackage').version
print('v2 {}'.format(v2))

## This one seems to be slower, and with pyinstaller makes the exe a lot bigger
from pbr.version import VersionInfo

v3 = VersionInfo('mypackage').release_string()
print('v3 {}'.format(v3))

您可以将其中之一直接放入__init__.py包中以提取版本信息,如下所示,类似于其他答案:

__all__ = (
    '__version__',
    'my_package_name'
)

import pkg_resources  # part of setuptools

__version__ = pkg_resources.get_distribution("mypackage").version

重新设定每首否决票的格式,以立即直接回答问题。
LightCC

1

在尝试寻找最简单可靠的解决方案几个小时后,以下是这些部分:

在包“ / mypackage”的文件夹内创建一个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__ = '1.2.7'

在setup.py中:

exec(open('mypackage/version.py').read())
setup(
    name='mypackage',
    version=__version__,

在主文件夹init .py中:

from .version import __version__

exec()函数在任何导入之外运行脚本,因为setup.py是在导入模块之前运行的。您仍然只需要在一个位置管理一个文件中的版本号,但是不幸的是,它不在setup.py中。(这是不利因素,但没有导入错误是有利因素)


1

自首次提出这个问题以来,已完成了大量的工作,以统一版本和支持约定。现在,《Python打包用户指南》中详细介绍了可口的选项。同样值得注意的是,按照PEP 440版本号方案在Python中相对严格,因此,要使程序包发布到Cheese Shop,保持良好状态就至关重要。

以下是版本控制选项的简化分类:

  1. setup.pysetuptools)中读取文件并获取版本。
  2. 使用外部构建工具(同时更新__init__.py和源代码控制),例如bump2versionchangeszest.releaser
  3. 将值设置__version__为特定模块中的全局变量。
  4. 将值放在简单的VERSION文本文件中,以便setup.py和代码读取。
  5. 通过setup.py发行版设置值,并使用importlib.metadata在运行时将其提取。(警告,有3.8之前和3.8之后的版本。)
  6. 将值设置为__version__in sample/__init__.py并在中导入样本setup.py
  7. 使用setuptools_scm从源代码管理中提取版本控制,以便它是规范参考,而不是代码。

注意,(7)可能是最现代的方法(构建元数据与代码无关,由自动化发布)。另外请注意,如果安装程序用于软件包发行,则简单程序python3 setup.py --version将直接报告版本。


-1

值得一说的是,如果您使用的是NumPy distutils,numpy.distutils.misc_util.Configuration则可以使用make_svn_version_py()一种将修订版号嵌入package.__svn_version__变量中的方法version


您能否提供更多详细信息或此操作方式的示例?
Stevoisiak

嗯 在2020年,这是(总是吗?)用于FORTRAN。软件包“ numpy.distutils是NumPy的一部分,它扩展了标准Python distutils以处理Fortran源。”
ingyhere

-1
  1. 使用一个version.py文件只用__version__ = <VERSION>该文件在参数。在setup.py文件中导入__version__参数,然后将其值放在setup.py文件中,如下所示: version=__version__
  2. 另一种方法是仅使用setup.py带有version=<CURRENT_VERSION>-的文件CURRENT_VERSION是硬编码的。

由于我们不想每次创建新标签(准备发布新的软件包版本)时都手动更改文件中的版本,因此可以使用以下内容。

我强烈建议使用bumpversion程序包。多年来,我一直在使用它来改进版本。

首先添加version=<VERSION>setup.py文件(如果尚未添加)。

每次更改版本时,都应使用如下简短脚本:

bumpversion (patch|minor|major) - choose only one option
git push
git push --tags

然后为每个仓库添加一个文件.bumpversion.cfg

[bumpversion]
current_version = <CURRENT_TAG>
commit = True
tag = True
tag_name = {new_version}
[bumpversion:file:<RELATIVE_PATH_TO_SETUP_FILE>]

注意:

  • 您可以像其他帖子中所建议的那样__version__version.py文件下使用参数,并像这样更新bumpversion文件: [bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
  • 必须 git commitgit reset您的回购中的所有内容,否则您将收到肮脏的回购错误。
  • 确保您的虚拟环境中包含了bumpversion程序包,如果没有,它将无法正常工作。

@cmcginty抱歉,延迟,请检查我的回答^^^-请注意,您必须git commit或git reset存储库中的所有内容,并确保您的虚拟环境包含的软件包bumpversion,否则它将无法正常工作。使用最新版本。
奥兰

对于此处建议的解决方案,我不清楚。您是否建议使用手动跟踪版本version.py或使用跟踪版本bumpversion
Stevoisiak

我建议使用@StevenVascellaro,不要使用手动版本控制。我试图解释的是,您可以直接使用umpfversion更新setup.py文件中的版本,或者更好地使用它来更新version.py文件。更新version.py文件并将__version__param值带入setup.py文件是更常见的做法。我的解决方案用于生产中,是一种常见的做法。注意:请注意,将bumpversion用作脚本的一部分是最好的解决方案,将其放在您的CI中,它将是自动操作。
奥兰

-3

如果使用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)

您建议将保存内容保存__version__到哪个文件?使用该解决方案,如何增加版本号?
Stevoisiak
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.