将版本嵌入Python包的标准方法?
是否有一种标准方法将版本字符串与 Python 包关联起来,以便我可以执行以下操作?
import foo
print(foo.version)
我想有某种方法可以检索该数据而无需任何额外的硬编码,因为次要/主要字符串已在 setup.py 中指定。 我发现的替代解决方案是在我的 foo/__init__.py 中导入 __version__ ,然后由 setup 生成 __version__.py .py。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(25)
对于其他一些答案,有一个稍微简单的替代方案:(
使用
str()
将版本号的自动递增部分转换为字符串将相当简单。)当然,从什么我发现,人们在使用 __version_info__ 时倾向于使用类似前面提到的版本,并将其存储为整数元组; 但是,我不太明白这样做的意义,因为我怀疑在某些情况下,除了出于好奇或自动增量之外,您还会出于任何目的对版本号的部分执行加法和减法等数学运算(即使如此,
int()
和str()
可以相当容易地使用)。 (另一方面,其他人的代码可能需要数字元组而不是字符串元组,从而失败。)当然,这是我自己的观点,我很乐意接受其他人关于使用数字元组的输入元组。
正如 shezi 提醒我的那样,数字字符串的(词法)比较不一定与直接数字比较具有相同的结果; 为此需要前导零。 因此最终,将 __version_info__ (或任何名称)存储为整数值元组将允许更有效的版本比较。
There is a slightly simpler alternative to some of the other answers:
(And it would be fairly simple to convert auto-incrementing portions of version numbers to a string using
str()
.)Of course, from what I've seen, people tend to use something like the previously-mentioned version when using
__version_info__
, and as such store it as a tuple of ints; however, I don't quite see the point in doing so, as I doubt there are situations where you would perform mathematical operations such as addition and subtraction on portions of version numbers for any purpose besides curiosity or auto-incrementation (and even then,int()
andstr()
can be used fairly easily). (On the other hand, there is the possibility of someone else's code expecting a numerical tuple rather than a string tuple and thus failing.)This is, of course, my own view, and I would gladly like others' input on using a numerical tuple.
As shezi reminded me, (lexical) comparisons of number strings do not necessarily have the same result as direct numerical comparisons; leading zeroes would be required to provide for that. So in the end, storing
__version_info__
(or whatever it would be called) as a tuple of integer values would allow for more efficient version comparisons.这里的许多解决方案都忽略了 git 版本标签,这仍然意味着您必须在多个位置跟踪版本(不好)。 我实现了以下目标:
git
存储库中的标记派生所有 python 版本引用git tag
/push
和setup.py upload 步骤使用不带任何输入的单个命令。
工作原理:
通过
make release
命令,可以找到 git 存储库中最后一个标记的版本并递增。 标签被推回到origin
。Makefile
将版本存储在src/_version.py
中,setup.py
会读取该版本,并包含在版本中。 不要将_version.py
签入源代码管理!setup.py
命令从package.__version__读取新版本字符串
.详细信息:
Makefile
release
目标始终递增第三个版本数字,但您可以使用next_minor_ver
或next_major_ver
递增其他数字。 这些命令依赖于签入存储库根目录的versionbump.py
脚本versionbump.py
这完成了如何处理和增加来自
git
的版本号的繁重工作。__init__.py
my_module/_version.py
文件导入到my_module/__init__.py
中。 将您想要与模块一起分发的任何静态安装配置放在这里。setup.py
最后一步是从
my_module
模块读取版本信息。当然,要使所有这些工作正常进行,您的存储库中必须至少有一个版本标签才能启动。
Many of these solutions here ignore
git
version tags which still means you have to track version in multiple places (bad). I approached this with the following goals:git
repogit tag
/push
andsetup.py upload
steps with a single command that takes no inputs.How it works:
From a
make release
command, the last tagged version in the git repo is found and incremented. The tag is pushed back toorigin
.The
Makefile
stores the version insrc/_version.py
where it will be read bysetup.py
and also included in the release. Do not check_version.py
into source control!setup.py
command reads the new version string frompackage.__version__
.Details:
Makefile
The
release
target always increments the 3rd version digit, but you can use thenext_minor_ver
ornext_major_ver
to increment the other digits. The commands rely on theversionbump.py
script that is checked into the root of the repoversionbump.py
This does the heavy lifting how to process and increment the version number from
git
.__init__.py
The
my_module/_version.py
file is imported intomy_module/__init__.py
. Put any static install config here that you want distributed with your module.setup.py
The last step is to read the version info from the
my_module
module.Of course, for all of this to work you'll have to have at least one version tag in your repo to start.
我在包目录中使用 JSON 文件。 这符合Zooko的要求。
pkg_dir/pkg_info.json
内部:setup.py
内部:
pkg_dir/__init__.py
内部:我还在 pkg_info 中放入了其他信息.json,如作者。 我
喜欢使用 JSON,因为我可以自动管理元数据。
I use a JSON file in the package dir. This fits Zooko's requirements.
Inside
pkg_dir/pkg_info.json
:Inside
setup.py
:Inside
pkg_dir/__init__.py
:I also put other information in
pkg_info.json
, like author. Ilike to use JSON because I can automate management of metadata.
自从首次提出这个问题以来,关于统一版本控制和支持约定的大量工作已经完成。。 Python 打包用户指南中现在详细提供了可口的选项。 另外值得注意的是,根据 PEP 440,Python 中的版本号方案相对严格,因此,如果您的包裹将被发布到 Cheese Shop,保持理智至关重要。
以下是版本控制选项的简短细分:
setup.py
中的文件 (setuptools) 并获取版本。__init__.py
以及源代码控制),例如 bump2version、更改 或 zest.releaser。__version__
全局变量。setup.py
版本设置值,并使用 importlib.metadata 在运行时拾取它。 (警告,有3.8之前和3.8之后的版本。)sample/__init__.py
中将值设置为__version__
,并在setup.py中导入sample
。注意(7)可能是最现代的方法(构建元数据独立于代码,由自动化发布)。 另外注意,如果安装程序用于软件包发布,则简单的
python3 setup.py --version
将直接报告版本。Lots of work toward uniform versioning and in support of conventions has been completed since this question was first asked. Palatable options are now detailed in the Python Packaging User Guide. Also noteworthy is that version number schemes are relatively strict in Python per PEP 440, and so keeping things sane is critical if your package will be released to the Cheese Shop.
Here's a shortened breakdown of versioning options:
setup.py
(setuptools) and get the version.__init__.py
as well as source control), e.g. bump2version, changes or zest.releaser.__version__
global variable in a specific module.setup.py
release, and use importlib.metadata to pick it up at runtime. (Warning, there are pre-3.8 and post-3.8 versions.)__version__
insample/__init__.py
and import sample insetup.py
.NOTE that (7) might be the most modern approach (build metadata is independent of code, published by automation). Also NOTE that if setup is used for package release that a simple
python3 setup.py --version
will report the version directly.另外值得注意的是,
__version__
是一个半标准。 在 python 中,__version_info__
也是一个元组,在简单的情况下,您可以执行以下操作:...并且您可以从文件中获取
__version__
字符串,或者任何。Also worth noting is that as well as
__version__
being a semi-std. in python so is__version_info__
which is a tuple, in the simple cases you can just do something like:...and you can get the
__version__
string from a file, or whatever.似乎没有一种标准方法可以在 python 包中嵌入版本字符串。 我见过的大多数软件包都使用您的解决方案的某些变体,即 eitner
将版本嵌入
setup.py
并让setup.py
生成一个模块(例如 < code>version.py) 仅包含由您的包导入的版本信息,或者相反:将版本信息放入您的包本身,然后导入 that 在
setup.py
中设置版本
There doesn't seem to be a standard way to embed a version string in a python package. Most packages I've seen use some variant of your solution, i.e. eitner
Embed the version in
setup.py
and havesetup.py
generate a module (e.g.version.py
) containing only version info, that's imported by your package, orThe reverse: put the version info in your package itself, and import that to set the version in
setup.py
arrow 以一种有趣的方式处理它。
现在(自2e5031b开始)
在<代码>arrow/__init__.py:
在
setup.py
中:之前
在
arrow/__init__.py
中:在
中setup.py:
arrow handles it in an interesting way.
Now (since 2e5031b)
In
arrow/__init__.py
:In
setup.py
:Before
In
arrow/__init__.py
:In
setup.py
:我还看到了另一种风格:
I also saw another style:
使用
setuptools
和pbr
没有管理版本的标准方法,但管理包的标准方法是
setuptools
。我发现的管理版本的最佳解决方案是使用
setuptools
和pbr
扩展。 现在这是我管理版本的标准方法。对于简单的项目来说,将项目设置为完整打包可能有点大材小用,但如果您需要管理版本,那么您可能处于正确的级别来设置所有内容。 这样做还可以使您的包可以在 PyPi 上发布,以便每个人都可以下载它并与 Pip 一起使用。
PBR 将大多数元数据从
setup.py
工具中移出并放入setup.cfg
文件中,然后将该文件用作大多数元数据(可以包括版本)的源。 如果需要的话,这允许使用诸如 pyinstaller 之类的东西将元数据打包到可执行文件中(如果是这样,您可能会需要此信息),并将元数据与其他包管理/设置脚本分开。 您可以直接手动更新setup.cfg
中的版本字符串,在构建软件包版本时,它将被拉入*.egg-info
文件夹中。 然后,您的脚本可以使用各种方法从元数据访问该版本(这些过程将在下面的部分中概述)。当使用 Git 进行 VCS/SCM 时,此设置甚至更好,因为它将从 Git 中提取大量元数据,以便您的存储库可以成为某些元数据的主要真实来源,包括版本、作者、更改日志、具体来说,对于版本,它将根据存储库中的 git 标签为当前提交创建版本字符串。
setup.py
和一个包含元数据的setup.cfg
文件。由于 PBR 会直接从您的 git 存储库中提取版本、作者、变更日志和其他信息,因此在为您的包创建发行版时,
setup.cfg
中的一些元数据可以被省略并自动生成(使用setup.py
)实时获取当前版本
setuptools
将使用setup.py
实时提取最新信息:这将根据最新提交以及存储库中存在的标签,从 setup.cfg 文件或 git 存储库中提取最新版本。 不过,此命令不会更新发行版中的版本。
更新版本元数据
当您使用
setup.py
创建发行版(例如py setup.py sdist
)时,所有当前信息将被提取并存储在分布。 这实际上是运行 setup.py --version 命令,然后将该版本信息存储到 package.egg-info 文件夹中的一组存储分发元数据的文件中。)脚本中的版本
您可以在包本身的 Python 脚本中访问当前版本的元数据。 例如,对于版本,到目前为止,我发现有几种方法可以做到这一点:
您可以将其中一种直接放入您的
__init__.py
中,以便包提取版本信息,如下所示,类似其他一些答案:Using
setuptools
andpbr
There is not a standard way to manage version, but the standard way to manage your packages is
setuptools
.The best solution I've found overall for managing version is to use
setuptools
with thepbr
extension. This is now my standard way of managing version.Setting up your project for full packaging may be overkill for simple projects, but if you need to manage version, you are probably at the right level to just set everything up. Doing so also makes your package releasable at PyPi so everyone can download and use it with Pip.
PBR moves most metadata out of the
setup.py
tools and into asetup.cfg
file that is then used as a source for most metadata, which can include version. This allows the metadata to be packaged into an executable using something likepyinstaller
if needed (if so, you will probably need this info), and separates the metadata from the other package management/setup scripts. You can directly update the version string insetup.cfg
manually, and it will be pulled into the*.egg-info
folder when building your package releases. Your scripts can then access the version from the metadata using various methods (these processes are outlined in sections below).When using Git for VCS/SCM, this setup is even better, as it will pull in a lot of the metadata from Git so that your repo can be your primary source of truth for some of the metadata, including version, authors, changelogs, etc. For version specifically, it will create a version string for the current commit based on git tags in the repo.
setup.py
and asetup.cfg
file with the metadata.As PBR will pull version, author, changelog and other info directly from your git repo, so some of the metadata in
setup.cfg
can be left out and auto generated whenever a distribution is created for your package (usingsetup.py
)Get the current version in real-time
setuptools
will pull the latest info in real-time usingsetup.py
:This will pull the latest version either from the
setup.cfg
file, or from the git repo, based on the latest commit that was made and tags that exist in the repo. This command doesn't update the version in a distribution though.Updating the version metadata
When you create a distribution with
setup.py
(i.e.py setup.py sdist
, for example), then all the current info will be extracted and stored in the distribution. This essentially runs thesetup.py --version
command and then stores that version info into thepackage.egg-info
folder in a set of files that store distribution metadata.Accessing the version from a script
You can access the metadata from the current build within Python scripts in the package itself. For version, for example, there are several ways to do this I have found so far:
You can put one of these directly in your
__init__.py
for the package to extract the version info as follows, similar to some other answers:使用 setuptools 和 pyproject.toml
setuptools 现在提供了一种动态获取版本的方法在
pyproject.toml
中重现此处的示例,您可以在
pyproject.toml
中创建类似以下内容的内容Using setuptools and pyproject.toml
Setuptools now offers a way to dynamically get version in
pyproject.toml
Reproducing the example here, you can create something like the following in your
pyproject.toml
经过几个小时的尝试找到最简单可靠的解决方案后,以下是以下部分:
在包“/mypackage”的文件夹内创建一个 version.py 文件:
在 setup.py 中:
在主文件夹 init.py:
exec()
函数在任何导入之外运行脚本,因为 setup.py 是在导入模块之前运行的。 你仍然只需要在一个地方管理一个文件中的版本号,但不幸的是它不在setup.py中。 (这是缺点,但没有导入错误是优点)After several hours of trying to find the simplest reliable solution, here are the parts:
create a version.py file INSIDE the folder of your package "/mypackage":
in setup.py:
in the main folder init.py:
The
exec()
function runs the script outside of any imports, since setup.py is run before the module can be imported. You still only need to manage the version number in one file in one place, but unfortunately it is not in setup.py. (that's the downside, but having no import bugs is the upside)不,没有一种标准方法可以将版本字符串嵌入到 Python 包中,以便可以将其作为属性进行访问。 PEP 396 – 模块版本号中提出了一项标准,但该 PEP 已于 2021 年被拒绝。
标准化内容:
非标准方法的几个示例是
VERSION
元组:或
__version__
字符串:越来越多地,您会发现许多项目根本不存储版本属性。。 无论项目是否将版本属性保留到源代码中,可靠地检索包版本的推荐方法是使用 stdlib
importlib.metadata
:如果您曾经为您的包提供过版本属性,并且如果您希望删除它,但出于向后兼容性的原因需要允许一段弃用期,您可以使用 模块level
__getattr__
后备:这是
python-attrs
使用的方法 23.1.0 例如。不要费心使用模块
__getattr__
方法,除非您要弃用旧项目中的现有版本属性。 对于新项目,我的建议是首先不要在源代码中提供版本属性,以便版本字符串的唯一真实来源位于包元数据中。No, there isn't a standard way to embed the version string in a Python package so that it's accessible as an attribute. A standard was proposed in PEP 396 – Module Version Numbers, but that PEP has been rejected in 2021.
What has been standardized:
pyproject.toml
(see Declaring project metadata and PEP 621 – Storing project metadata inpyproject.toml
)A couple of examples of non-standard approaches are a
VERSION
tuple:or a
__version__
string:Increasingly, you'll find that many projects don't store a version attribute at all. Regardless of whether a project keeps a version attribute burnt into the source code, the recommended way to retrieve a package version reliably is by using stdlib
importlib.metadata
:If you've historically provided a version attribute for your package, and you wish to remove it but need to allow a deprecation period for backwards-compatibility reasons, you may use a module level
__getattr__
fallback:This is the approach used by
python-attrs
23.1.0 for example.Don't bother using the module
__getattr__
approach unless you're deprecating an existing version attribute from an old project. For new projects, my recommendation is not to provide a version attribute in the source code in the first place, so that the single source of truth for the version string is in the package metadata.pbr 与凹凸2版本
此解决方案源自本文。
用例 - 通过 PyInstaller 分发的 python GUI 包。 需要显示版本信息。
这是项目
packagex
的结构,其中
setup.py
是packagex/_version.py
只包含packagex/__init__.py
和
.bumpversion.cfg
以及 这里 是一个 GitHub使用此设置的项目。
pbr with bump2version
This solution was derived from this article.
The use case - python GUI package distributed via PyInstaller. Needs to show version info.
Here is the structure of the project
packagex
where
setup.py
ispackagex/_version.py
contains justand
packagex/__init__.py
and for
.bumpversion.cfg
And here is a GitHub project with this setup.
看到这么多答案清楚地表明没有标准方法。
所以这是另一个:
Poetry 和 importlib
我使用 poetry-dynamic-versioning用于设置
poetry build
版本的插件。然后,在我的包的 __init__.py 中,我有:
当然,这需要有正确的包结构和构建过程。
Seeing so many answers is a clean sign that there is no standard way.
So here's another:
Poetry and importlib
I use the poetry-dynamic-versioning plugin to set the version on
poetry build
.Then in the
__init__.py
of my package I have:Of course this requires to have a proper package structure and build process.
我更喜欢从安装环境中读取软件包版本。
这是我的
src/foo/_version.py
:确保
foo
始终已安装,这就是为什么需要src/
层来防止 < code>foo 无需安装即可导入。在
setup.py
中,我使用 setuptools-scm 来自动生成版本。更新于2022.7.5:
还有另一种方式,这是我现在最喜欢的。 使用
setuptools-scm
生成_version.py
文件。I prefer to read the package version from installation environment.
This is my
src/foo/_version.py
:Makesure
foo
is always already installed, that's why asrc/
layer is required to preventfoo
imported without installation.In the
setup.py
, I use setuptools-scm to generate the version automatically.Update in 2022.7.5:
There is another way, which is my faviourate now. Use
setuptools-scm
to generate a_version.py
file.__init__.py
相同的文件夹中创建一个名为_version.txt
的文件,并将版本写入单行:_version.txt< 中读取此信息/code> 在
__init__.py
中:_version.txt
in the same folder as__init__.py
and write version as a single line:_version.txt
in__init__.py
:将
__version__="0.0.1"
添加到您的主__init__.py
中:如果您的库名为
mylib
,那么这是唯一的事实来源您可以通过mylib.__version__
直接访问您的版本号。 但您仍然需要将其添加到您的 setup.py 文件中。在
setup.py
中,您需要将__init__.py
作为文本文件读取:有多种方法可以从以下位置读取
__version__
变量:__init__.py
作为文本文件。 以这个为例: 单一采购软件包版本请记住,您需要避免以下几点:
__init__.py
导入__version__
因为这会加载所有子模块及其依赖项在设置时不可用并且会失败。__init__.py< /code> 位于),因为它会自动触发
__init__.py
这将导致与第 1 点中提到的相同问题。Add
__version__="0.0.1"
to your main__init__.py
:If your library is called
mylib
, this is the single source of truth for your version number which you can directly access bymylib.__version__
. But you still need to add it to yoursetup.py
file.In your
setup.py
, you need to read the__init__.py
as a text file:There are different ways to read the
__version__
variable from__init__.py
as a text file. Check this one as an example: Single-sourcing the package versionPlease keep in mind that you need the avoid the following points:
__version__
from__init__.py
because this loads all sub-modules with their dependencies that are not available at the setup time and will fail.__version__
in a separate file (e.g.version.py
) if you are going to import it from your main directory (i.e. where your__init__.py
is located) because it will automatically trigger__init__.py
which will lead to the same issue mentioned in point 1.__version__ =
参数的version.py
文件。 在setup.py
文件中导入__version__
参数并将其值放入setup.py
文件中,如下所示:version=__version__
version=
的setup.py
文件 - CURRENT_VERSION 是硬编码的。由于我们不想每次创建新标签(准备发布新的包版本)时都手动更改文件中的版本,因此我们可以使用以下内容。
我强烈推荐 bumpversion 包。 我多年来一直使用它来升级版本。
首先将
version=
添加到您的setup.py
文件(如果您还没有)。每次升级版本时,您都应该使用这样的简短脚本:
然后为每个存储库添加一个文件,名为:
.bumpversion.cfg
:注意:
像其他帖子中建议的那样,在
参数,并像这样更新凹凸版本文件:version.py
文件下添加 __version__[bumpversion:file:]
git commit
或git Reset
存储库中的所有内容,否则你会得到一个肮脏的回购错误。version.py
file only with__version__ = <VERSION>
param in the file. In thesetup.py
file import the__version__
param and put it's value in thesetup.py
file like this:version=__version__
setup.py
file withversion=<CURRENT_VERSION>
- the CURRENT_VERSION is hardcoded.Since we don't want to manually change the version in the file every time we create a new tag (ready to release a new package version), we can use the following..
I highly recommend bumpversion package. I've been using it for years to bump a version.
start by adding
version=<VERSION>
to yoursetup.py
file if you don't have it already.You should use a short script like this every time you bump a version:
Then add one file per repo called:
.bumpversion.cfg
:Note:
__version__
parameter underversion.py
file like it was suggested in other posts and update the bumpversion file like this:[bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
git commit
orgit reset
everything in your repo, otherwise you'll get a dirty repo error.如果您的项目使用 git 或 Mercurial 作为其 SCM,我建议您执行以下操作:
如下所示配置您的
__init__.py
,以便它始终设置__version__
属性正确 - 无论是在开发模式、非 pip 开发模式(由 IDE 更新的 python 路径)或生产模式(pip install)下然后,选择一种方式来生成生产中所需的
_version.py
文件(上面第 4 行导入的文件)。a. 我个人在
setup.py
中执行此操作:b. 或者,如果您更喜欢使用
pyproject.toml
或setup.cfg
文件配置项目,您可以查看 setuptools_scm 了解这是如何完成的。 例如,对于pyproject.toml
,它是这样的:<前><代码>[tool.setuptools_scm]/_version.py"
write_to = "
c. 最后,另一种方法是当您希望创建版本时手动执行脚本。 该脚本必须在对项目进行 git 标记之后、发布之前运行。
例如,您可以在持续集成过程中将其作为单个命令行运行:
这种模式 (1+2a) 在过去几年中成功地用于我的数十个已发布的软件包(例如 makefun),所以我可以热烈推荐它。
注意:我最初在一个单独的网页 此处< /a>.
编辑:
setuptools_scm
的文档指出write_to
现已弃用(但在撰写本文时仍受支持)。 他们建议改用version_file
。If your project uses git or mercurial as its SCM, I would recommend the following:
Configure your
__init__.py
as shown below so that it always sets the__version__
attribute correctly - both in development mode, non-pip development mode (python path updated by IDE), or production mode (pip install)then, choose a way to generate the
_version.py
file that is required in production (the one imported on line 4 above).a. I personally do this in
setup.py
:b. Alternately if you prefer to configure your project using a
pyproject.toml
orsetup.cfg
file, you can check the documentation in setuptools_scm to find how this is done. For example withpyproject.toml
it is like this:c. Finally, an alternative is to execute a script manually when you wish to create releases. This script must run after git-tagging your project and before publishing it.
you can for example run this as a single commandline in your continuous integration process:
This pattern (1+2a) has worked successfully for me for dozens of published packages over the past years (for example makefun), so I can warmly recommend it.
Note: I originally provided the above tip in a separate web page here.
EDIT:
setuptools_scm
's documentation states thatwrite_to
is now deprecated (but still supported at the time of writing). They recommend to useversion_file
instead.如果您使用 CVS(或 RCS)并想要快速解决方案,您可以使用:(
当然,修订号将由 CVS 代替您。)
这为您提供了一个适合打印的版本和您可以使用的版本信息检查您正在导入的模块是否至少具有预期的版本:
If you use CVS (or RCS) and want a quick solution, you can use:
(Of course, the revision number will be substituted for you by CVS.)
This gives you a print-friendly version and a version info that you can use to check that the module you are importing has at least the expected version:
就其价值而言,如果您使用 NumPy distutils,
numpy.distutils.misc_util.Configuration
有一个make_svn_version_py()
方法,将修订号嵌入到package.__svn_version__ 在变量
version
中。For what it's worth, if you're using NumPy distutils,
numpy.distutils.misc_util.Configuration
has amake_svn_version_py()
method that embeds the revision number insidepackage.__svn_version__
in the variableversion
.不是直接回答您的问题,但您应该考虑将其命名为
__version__
,而不是version
。这几乎是一个准标准。 标准库中的许多模块都使用
__version__
,这也用于 很多 3rd-party模块,所以它是准标准。通常,
__version__
是一个字符串,但有时它也是一个浮点数或元组。正如 S.Lott 提到的(谢谢!), PEP 8 明确指出:
您还应该确保版本号符合 PEP 440 (PEP 386 该标准的先前版本)。
Not directly an answer to your question, but you should consider naming it
__version__
, notversion
.This is almost a quasi-standard. Many modules in the standard library use
__version__
, and this is also used in lots of 3rd-party modules, so it's the quasi-standard.Usually,
__version__
is a string, but sometimes it's also a float or tuple.As mentioned by S.Lott (Thank you!), PEP 8 says it explicitly:
You should also make sure that the version number conforms to the format described in PEP 440 (PEP 386 a previous version of this standard).
我使用单个
_version.py
文件作为“曾经规范的位置”来存储版本信息:它提供了一个
__version__
属性。它提供标准元数据版本。 因此,它将被
pkg_resources
或其他解析包元数据的工具(EGG-INFO 和/或 PKG-INFO,PEP 0345)检测到。在构建包时,它不会导入您的包(或其他任何内容),这在某些情况下可能会导致问题。 (有关这可能导致什么问题,请参阅下面的评论。)
只有一处写下了版本号,因此当版本号发生变化时,只有一处可以更改它,并且版本号发生变化的机会较小。版本不一致。
它的工作原理如下:存储版本号的“一个规范位置”是一个名为“_version.py”的 .py 文件,该文件位于您的 Python 包中,例如
myniftyapp/_version.py
。 这个文件是一个Python模块,但是你的setup.py没有导入它! (这会击败功能 3。)相反,您的 setup.py 知道该文件的内容非常简单,例如:因此您的 setup.py 打开该文件并解析它,代码如下:
然后您的 setup.py 通过该字符串作为
setup()
的“version”参数的值,从而满足功能 2。为了满足功能 1,您可以拥有您的包(在运行时,而不是在安装时!)从
myniftyapp/__init__.py
导入 _version 文件,如下所示:这里是 我多年来一直使用这种技术的示例。
该示例中的代码有点复杂,但我在该注释中写入的简化示例应该是一个完整的实现。
这是导入版本的示例代码< /a>.
如果您发现这种方法有任何问题,请告诉我。
I use a single
_version.py
file as the "once cannonical place" to store version information:It provides a
__version__
attribute.It provides the standard metadata version. Therefore it will be detected by
pkg_resources
or other tools that parse the package metadata (EGG-INFO and/or PKG-INFO, PEP 0345).It doesn't import your package (or anything else) when building your package, which can cause problems in some situations. (See the comments below about what problems this can cause.)
There is only one place that the version number is written down, so there is only one place to change it when the version number changes, and there is less chance of inconsistent versions.
Here is how it works: the "one canonical place" to store the version number is a .py file, named "_version.py" which is in your Python package, for example in
myniftyapp/_version.py
. This file is a Python module, but your setup.py doesn't import it! (That would defeat feature 3.) Instead your setup.py knows that the contents of this file is very simple, something like:And so your setup.py opens the file and parses it, with code like:
Then your setup.py passes that string as the value of the "version" argument to
setup()
, thus satisfying feature 2.To satisfy feature 1, you can have your package (at run-time, not at setup time!) import the _version file from
myniftyapp/__init__.py
like this:Here is an example of this technique that I've been using for years.
The code in that example is a bit more complicated, but the simplified example that I wrote into this comment should be a complete implementation.
Here is example code of importing the version.
If you see anything wrong with this approach, please let me know.
重写 2017-05
经过 13 年多的编写 Python 代码和管理各种包之后,我得出的结论是 DIY 可能不是最好的方法。
我开始使用
pbr
包来处理包中的版本控制。 如果您使用 git 作为您的 SCM,这将像魔术一样适合您的工作流程,节省您数周的工作(您会惊讶于问题的复杂性)。截至今天,pbr 每月下载量为 1200 万次,达到这个水平并不包含任何肮脏的伎俩。 这只是一件事——以一种非常简单的方式解决常见的包装问题。
pbr
可以承担更多的包维护负担,并且不限于版本控制,但它并不强迫您采用它的所有好处。因此,为了让您了解如何在一次提交中采用 pbr,请查看 将打包切换到 pbr
您可能会发现该版本根本没有存储在存储库中。 PBR 确实从 Git 分支和标签中检测到它。
无需担心没有 git 存储库时会发生什么,因为 pbr 在打包或安装应用程序时会“编译”并缓存版本,因此对 git 没有运行时依赖。
旧解决方案
这是迄今为止我见过的最好的解决方案,它也解释了原因:
Inside
yourpackage/version.py
:Inside
yourpackage/__init__.py
:Inside
setup.py
:如果您知道另一种似乎更好的方法,请告诉我。
Rewritten 2017-05
After 13+ years of writing Python code and managing various packages, I came to the conclusion that DIY is maybe not the best approach.
I started using the
pbr
package for dealing with versioning in my packages. If you are using git as your SCM, this will fit into your workflow like magic, saving your weeks of work (you will be surprised about how complex the issue can be).As of today, pbr has 12M mongthly downloads, and reaching this level didn't include any dirty tricks. It was only one thing -- fixing a common packaging problem in a very simple way.
pbr
can do more of the package maintenance burden, and is not limited to versioning, but it does not force you to adopt all its benefits.So to give you an idea about how it looks to adopt pbr in one commit have a look switching packaging to pbr
Probably you would observed that the version is not stored at all in the repository. PBR does detect it from Git branches and tags.
No need to worry about what happens when you do not have a git repository because pbr does "compile" and cache the version when you package or install the applications, so there is no runtime dependency on git.
Old solution
Here is the best solution I've seen so far and it also explains why:
Inside
yourpackage/version.py
:Inside
yourpackage/__init__.py
:Inside
setup.py
:If you know another approach that seems to be better let me know.
根据推迟的[停止新闻:拒绝] PEP 396 (模块版本号),有一个建议的方法可以做到这一点。 它描述了模块要遵循的(诚然是可选的)标准,并给出了基本原理。 这是一个片段:
Per the deferred [STOP PRESS: rejected] PEP 396 (Module Version Numbers), there is a proposed way to do this. It describes, with rationale, an (admittedly optional) standard for modules to follow. Here's a snippet: