我怎样才能得到我的包中setup.py(setuptools)中定义的版本?

我怎么能从我的包中获取setup.py定义的版本(for --version或其他目的)?

询问已经安装的发行版本的string

要在运行时从您的包中检索版本(您的问题看起来真的在问什么),可以使用:

 import pkg_resources # part of setuptools version = pkg_resources.require("MyProject")[0].version 

存储版本string以在安装期间使用

如果你想转到另一个方向(这似乎是其他答案的作者在这里似乎认为你问),把版本string放在一个单独的文件,并阅读setup.py文件的内容。

你可以使用__version__行在你的包中创build一个version.py,然后使用execfile('mypackage/version.py')从setup.py中读取它,以便在setup.py命名空间中设置__version__

如果你想要一个更简单的方法来处理所有的Python版本,甚至可能需要访问版本string的非Python语言:

将版本string存储为纯文本文件(例如VERSION的唯一内容,并在setup.py读取该文件。

 version_file = open(os.path.join(mypackage_root_dir, 'VERSION')) version = version_file.read().strip() 

然后,相同的VERSION文件就可以在任何其他程序中正常工作,甚至是非Python VERSION文件,只需要在一个地方为所有程序更改版本string。

安装期间关于竞赛状况的警告

顺便说一下,请不要从你的setup.py中导入你的包,正如另一个答案中的build议:它似乎适用于你(因为你已经有你的包的依赖关系安装),但它会对你的包的新用户造成严重破坏,因为他们将无法安装您的软件包,而无需先手动安装依赖关系。

示例研究: mymodule

想象一下这个configuration:

 setup.py mymodule/ / __init__.py / version.py / myclasses.py 

然后想象一下通常情况下你有依赖和setup.py样子:

 setup(... install_requires=['dep1','dep2', ...] ...) 

还有一个例子__init__.py

 from mymodule.myclasses import * from mymodule.version import __version__ 

例如myclasses.py

 # these are not installed on your system. # importing mymodule.myclasses would give ImportError import dep1 import dep2 

问题1:在安装过程中导入mymodule

如果您的setup.py导入mymodule然后在安装过程中很可能会得到一个ImportError 。 当你的包依赖时,这是一个非常常见的错误。 如果你的软件包没有内build的其他依赖项,你可能是安全的; 然而这不是一个好的做法。 其原因是它不是面向未来的; 说明天你的代码需要消耗一些其他的依赖。

问题2:我的__version__在哪里?

如果您在setup.py硬编码__version__ ,那么它可能与您在模块中提供的版本不匹配。 为了保持一致,你可以把它放在一个地方,当你需要的时候从同一个地方阅读。 使用import你可能会得到问题#1。

解决scheme:àla setuptools

你可以使用openexec的组合,并为exec提供一个字典来添加variables:

 # setup.py from setuptools import setup, find_packages from distutils.util import convert_path main_ns = {} ver_path = convert_path('mymodule/version.py') with open(ver_path) as ver_file: exec(ver_file.read(), main_ns) setup(..., version=main_ns['__version__'], ...) 

并在mymodule/version.py公开版本:

 __version__ = 'some.semantic.version' 

这样,版本随模块一起提供,并且在安装期间尝试导入缺less依赖项(尚未安装)的模块时没有问题。

最好的方法是在你的产品代码中定义__version__ ,然后从那里导入setup.py。 这给你一个值,你可以在你的运行模块中读取,并且只有一个地方来定义它。

setup.py中的值没有安装,setup.py在安装后不会停留。

我在coverage.py中做了什么(例如):

 # coverage/__init__.py __version__ = "3.2" # setup.py from coverage import __version__ setup( name = 'coverage', version = __version__, ... ) 

UPDATE(2017):coverage.py不再导入自己的版本。 导入你自己的代码可以使它不能安装,因为你的产品代码将尝试导入依赖项,这些依赖项还没有安装,因为setup.py是安装它们的东西。

你的问题有点含糊,但是我想你要问的是如何指定它。

你需要像这样定义__version__

 __version__ = '1.4.4' 

然后你可以确认setup.py知道你刚刚指定的版本:

 % ./setup.py --version 1.4.4 

我不满意这些答案…不想要求setuptools,也没有为单个variables创build一个完整的单独模块,所以我想出了这些。

当你确定主要的模块是pep8风格,并保持这种方式:

 version = '0.30.unknown' with file('mypkg/mymod.py') as f: for line in f: if line.startswith('__version__'): _, _, version = line.replace("'", '').split() break 

如果您想要特别小心并使用真正的parsing器:

 import ast version = '0.30.unknown2' with file('mypkg/mymod.py') as f: for line in f: if line.startswith('__version__'): version = ast.parse(line).body[0].value.s break 

setup.py是一个一次性模块,所以不是一个问题,如果它有点难看。

 print `version` 

在源代码树中创build一个文件,例如在yourbasedir / yourpackage / _version.py中。 让该文件只包含一行代码,如下所示:

__version__ = "1.1.0-r4704"

然后在你的setup.py中,打开这个文件,parsing出这个版本号:

 verstr =“未知”
尝试:
     verstrline = open('yourpackage / _version.py',“rt”)。read()
 EnvironmentError除外:
    通过#好吧,没有版本文件。
其他:
     VSRE = r“^ __ version__ = ['\”]([^'\“] *)['\”]“
     mo = re.search(VSRE,verstrline,re.M)
    如果mo:
         verstr = mo.group(1)
    其他:
        引发RuntimeError(“无法在你的软件包/ _version.py中find版本”)

最后,在yourbasedir/yourpackage/__init__.py导入_version如下:

 __version__ =“未知”
尝试:
     from _version import __version__
除了ImportError:
     #我们正在运行一个没有_version.py的树,所以我们不知道我们的版本是什么。
    通过

这样做的代码的一个例子是我所维护的“pyutil”包。 (请参阅PyPI或谷歌search – stackoverflow不允许我在这个答案中包括一个超链接。

@pjeby是正确的,你不应该从它自己的setup.py中导入你的包。 这将工作,当你testing它创build一个新的Python解释器,并执行setup.py第一件事情: python setup.py ,但有些情况下,它将无法正常工作。 这是因为import youpackage并不意味着要读取一个名为“yourpackage”的目录的当前工作目录,这意味着在当前的sys.modules查找关键字“yourpackage”,然后做不同的事情。 所以当你执行python setup.py时,它总是有效的,因为你有一个新的,空的sys.modules ,但是这通常不起作用。

例如,如果py2exe正在执行你的setup.py作为打包应用程序的一部分呢? 我见过这样的情况,py2exe会把一个错误的版本号放在一个包上,因为这个包的setup.py文件是从import myownthing中得到它的版本号的,但是这个包的另一个版本在py2exe跑。 同样,如果setuptools,easy_install,distribute或distutils2正在尝试构build您的软件包,作为安装依赖于您的不同软件包的过程的一部分呢? 然后,在您的setup.py被评估的时候,您的包是否可导入,或者在这个Python解释器的生命周期中是否已经导入了您的包的版本,或者导入包是否需要先安装其他包,或者有副作用,可以改变结果。 我试图重新使用Python包,导致py2exe和setuptools等工具出现问题,因为他们的setup.py会自行导入包以find它的版本号。

顺便说一下,这种技术可以很好地利用工具自动为你创buildyourpackage/_version.py文件,例如通过读取你的版本控制历史,并根据版本控制历史中最新的标签写出一个版本号。 下面是一个为darcs做的工具: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst这里是一个代码片段,它为git做同样的事情:; http:// github .COM /华纳/python-ECDSA / BLOB / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py#L34

为了避免导入文件(从而执行其代码),可以parsing它并从语法树中恢复version属性:

 # assuming 'path' holds the path to the file import ast with open(path, 'rU') as file: t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST) for node in (n for n in t.body if isinstance(n, ast.Assign)): if len(node.targets) == 1: name = node.targets[0] if isinstance(name, ast.Name) and \ name.id in ('__version__', '__version_info__', 'VERSION'): v = node.value if isinstance(v, ast.Str): version = vs break if isinstance(v, ast.Tuple): r = [] for e in v.elts: if isinstance(e, ast.Str): r.append(es) elif isinstance(e, ast.Num): r.append(str(en)) version = '.'.join(r) break 

这段代码试图在模块的顶层find__version__VERSION赋值,返回的是string值。 右侧可以是一个string或一个元组。

这也应该使用正则expression式,并根据元数据字段有这样的格式:

 __fieldname__ = 'value' 

在setup.py的开头使用以下内容:

 import re main_py = open('yourmodule.py').read() metadata = dict(re.findall("__([az]+)__ = '([^']+)'", main_py)) 

之后,您可以像这样在脚本中使用元数据:

 print 'Author is:', metadata['author'] print 'Version is:', metadata['version'] 

有一千种方法来剥皮猫 – 这是我的:

 # Copied from (and hacked): # https://github.com/pypa/virtualenv/blob/develop/setup.py#L42 def get_version(filename): import os import re here = os.path.dirname(os.path.abspath(__file__)) f = open(os.path.join(here, filename)) version_file = f.read() f.close() version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") 

从@ gringo-suave清理https://stackoverflow.com/a/12413800

 from itertools import ifilter from os import path from ast import parse with open(path.join('package_name', '__init__.py')) as f: __version__ = parse(next(ifilter(lambda line: line.startswith('__version__'), f))).body[0].value.s 

现在这是毛病,需要一些改进(甚至有可能是我错过了pkg_resources中的一个未被发现的成员调用),但我不明白为什么这不起作用,也不知道为什么没有人build议它(迄今为止search)没有把这个了)…注意,这是Python 2.x,并且需要pkg_resources(叹气):

 import pkg_resources version_string = None try: if pkg_resources.working_set is not None: disto_obj = pkg_resources.working_set.by_key.get('<my pkg name>', None) # (I like adding ", None" to gets) if disto_obj is not None: version_string = disto_obj.version except Exception: # Do something pass