如何在我的包中获取setup.py中定义的版本(setuptools)?
我该如何从我的包中获取在 setup.py
文件里定义的版本号(比如用于 --version
或其他用途)呢?
21 个回答
最好的方法是在你的产品代码里定义一个叫 __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__,
...
)
更新(2017年):coverage.py 现在不再通过导入自己来获取版本号了。导入自己的代码可能会导致无法安装,因为你的产品代码会尝试导入一些依赖项,而这些依赖项还没有被安装,因为是 setup.py 来负责安装它们的。
示例研究:mymodule
想象一下这样的配置:
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
错误。这是一个很常见的错误,尤其是当你的包有依赖项时。如果你的包除了内置库之外没有其他依赖项,可能会安全一些;但这并不是一个好习惯。原因在于,这样做不够稳妥;比如说,明天你的代码需要使用其他依赖项。
问题 #2:我的 __version__
在哪里?
如果你在 setup.py
中硬编码了 __version__
,那么它可能和你在模块中发布的版本不一致。为了保持一致性,你应该把它放在一个地方,并在需要的时候从同一个地方读取。使用 import
的话,你可能会遇到问题 #1。
解决方案:使用 setuptools
你可以结合使用 open
和 exec
,并为 exec
提供一个字典来添加变量:
# 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'
这样,版本信息就和模块一起发布了,你在设置时就不会因为导入缺少依赖的模块而遇到问题(这些依赖还没有安装)。
查询已安装分发版的版本信息
如果你想在运行时从你的包中获取版本信息(这似乎是你问题的核心),你可以使用:
import pkg_resources # part of setuptools
version = pkg_resources.require("MyProject")[0].version
在安装时存储版本信息
如果你想反过来做(这似乎是其他回答者认为你在问的),可以把版本信息放在一个单独的文件里,然后在 setup.py
中读取这个文件的内容。
你可以在你的包里创建一个 version.py 文件,里面写上 __version__
这一行,然后在 setup.py 中用 execfile('mypackage/version.py')
来读取它,这样就能在 setup.py 的命名空间中设置 __version__
。
关于安装时竞争条件的警告
顺便提一下,不要像这里其他回答中建议的那样从 setup.py 中导入你的包:这看起来对你是有效的(因为你已经安装了包的依赖),但对新用户来说会造成很大麻烦,因为他们在安装你的包时必须先手动安装依赖。