简体   繁体   English

如何在我的 package 中获取 setup.py (setuptools) 中定义的版本?

[英]How can I get the version defined in setup.py (setuptools) in my package?

How could I get the version defined in setup.py from my package (for --version , or other purposes)?我如何从我的 package 中获取setup.py中定义的版本(用于--version或其他目的)?

Interrogate version string of already-installed distribution查询已安装发行版的版本字符串

To retrieve the version from inside your package at runtime (what your question appears to actually be asking), you can use:要在运行时从包内部检索版本(您的问题实际上是在问什么),您可以使用:

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

Store version string for use during install存储版本字符串以供安装期间使用

If you want to go the other way 'round (which appears to be what other answer authors here appear to have thought you were asking), put the version string in a separate file and read that file's contents in setup.py .如果您想反其道而行之(这似乎是其他答案作者似乎认为您在问的问题),请将版本字符串放在单独的文件中,并在setup.py中读取该文件的内容。

You could make a version.py in your package with a __version__ line, then read it from setup.py using execfile('mypackage/version.py') , so that it sets __version__ in the setup.py namespace.您可以在包中使用__version__行创建一个 version.py,然后使用execfile('mypackage/version.py')从 setup.py 中读取它,以便在 setup.py 命名空间中设置__version__

Warning about race condition during install安装期间有关竞争条件的警告

By the way, DO NOT import your package from your setup.py as suggested in another answer here: it will seem to work for you (because you already have your package's dependencies installed), but it will wreak havoc upon new users of your package, as they will not be able to install your package without manually installing the dependencies first.顺便说一句,不要按照此处另一个答案中的建议从 setup.py 导入您的包:它似乎对您有用(因为您已经安装了包的依赖项),但它会对您的包的新用户造成严重破坏,因为如果不先手动安装依赖项,他们将无法安装您的包。

example study: mymodule示例研究: mymodule

Imagine this configuration:想象一下这样的配置:

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

Then imagine some usual scenario where you have dependencies and setup.py looks like:然后想象一些常见的场景,你有依赖项, setup.py看起来像:

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

And an example __init__.py :还有一个例子__init__.py

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

And for example myclasses.py :例如myclasses.py

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

problem #1: importing mymodule during setup问题 #1:在安装过程中导入mymodule

If your setup.py imports mymodule then during setup you would most likely get an ImportError .如果您的setup.py导入mymodule ,那么在设置期间您很可能会收到ImportError This is a very common error when your package has dependencies.当您的包具有依赖项时,这是一个非常常见的错误。 If your package does not have other dependencies than the builtins, you may be safe;如果你的包除了内置之外没有其他依赖项,你可能是安全的; however this isn't a good practice.但这不是一个好习惯。 The reason for that is that it is not future-proof;原因是它不是面向未来的。 say tomorrow your code needs to consume some other dependency.说明天你的代码需要消耗一些其他的依赖。

problem #2: where's my __version__ ?问题 #2:我的__version__呢?

If you hardcode __version__ in setup.py then it may not match the version that you would ship in your module.如果您在setup.py中对__version__进行硬编码,那么它可能与您在模块中发布的版本不匹配。 To be consistent, you would put it in one place and read it from the same place when you need it.为了保持一致,您可以将它放在一个地方,并在需要时从同一个地方读取它。 Using import you may get the problem #1.使用import您可能会遇到问题 #1。

solution: à la setuptools解决方案:à la setuptools

You would use a combination of open , exec and provide a dict for exec to add variables:您将使用openexec的组合并为exec提供一个 dict 来添加变量:

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

And in mymodule/version.py expose the version:mymodule/version.py中公开版本:

__version__ = 'some.semantic.version'

This way, the version is shipped with the module, and you do not have issues during setup trying to import a module that has missing dependencies (yet to be installed).这样,该版本随模块一起提供,并且您在安装过程中尝试导入缺少依赖项(尚未安装)的模块时不会遇到问题。

The best technique is to define __version__ in your product code, then import it into setup.py from there.最好的技术是在您的产品代码中定义__version__ ,然后从那里将其导入 setup.py。 This gives you a value you can read in your running module, and have only one place to define it.这为您提供了一个可以在运行模块中读取的值,并且只有一个地方可以定义它。

The values in setup.py are not installed, and setup.py doesn't stick around after installation. setup.py 中的值未安装,并且 setup.py 安装后不会保留。

What I did (for example) in coverage.py:我在coverage.py中做了什么(例如):

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

setup(
    name = 'coverage',
    version = __version__,
    ...
    )

UPDATE (2017): coverage.py no longer imports itself to get the version.更新(2017 年):coverage.py 不再导入自身来获取版本。 Importing your own code can make it uninstallable, because you product code will try to import dependencies, which aren't installed yet, because setup.py is what installs them.导入您自己的代码可以使其可卸载,因为您的产品代码将尝试导入尚未安装的依赖项,因为 setup.py 是安装它们的。

Your question is a little vague, but I think what you are asking is how to specify it.你的问题有点模糊,但我认为你要问的是如何指定它。

You need to define __version__ like so:您需要像这样定义__version__

__version__ = '1.4.4'

And then you can confirm that setup.py knows about the version you just specified:然后您可以确认 setup.py 知道您刚刚指定的版本:

% ./setup.py --version
1.4.4

I wasn't happy with these answers... didn't want to require setuptools, nor make a whole separate module for a single variable, so I came up with these.我对这些答案不满意......不想要求 setuptools,也不想为单个变量制作一个完整的单独模块,所以我想出了这些。

For when you are sure the main module is in pep8 style and will stay that way:因为当你确定主模块是 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

If you'd like to be extra careful and use a real parser:如果您想格外小心并使用真正的解析器:

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 is somewhat of a throwaway module so not an issue if it is a bit ugly. setup.py 有点像一次性模块,所以如果它有点难看,这不是问题。


Update: funny enough I've moved away from this in recent years and started using a separate file in the package called meta.py .更新:有趣的是,近年来我已经摆脱了这一点,并开始在包中使用一个名为meta.py的单独文件。 I put lots of meta data in there that I might want to change frequently.我在那里放了很多我可能想要经常更改的元数据。 So, not just for one value.所以,不仅仅是一个价值。

With a structure like this:使用这样的结构:

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

where version.py contains:其中version.py包含:

__version__ = 'version_string'

You can do this in setup.py :您可以在setup.py中执行此操作:

import sys

sys.path[0:0] = ['mymodule']

from version import __version__

This won't cause any problem with whatever dependencies you have in your mymodule/__init__.py这不会对您的 mymodule/__init__.py 中的任何依赖项造成任何问题

Create a file in your source tree, eg in yourbasedir/yourpackage/_version.py .在您的源代码树中创建一个文件,例如在 yourbasedir/yourpackage/_version.py 中。 Let that file contain only a single line of code, like this:让该文件只包含一行代码,如下所示:

__version__ = "1.1.0-r4704"

Then in your setup.py, open that file and parse out the version number like this:然后在 setup.py 中,打开该文件并解析出版本号,如下所示:

verstr = "unknown"
try:
    verstrline = open('yourpackage/_version.py', "rt").read()
except EnvironmentError:
    pass # Okay, there is no version file.
else:
    VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
    mo = re.search(VSRE, verstrline, re.M)
    if mo:
        verstr = mo.group(1)
    else:
        raise RuntimeError("unable to find version in yourpackage/_version.py")

Finally, in yourbasedir/yourpackage/__init__.py import _version like this:最后,在yourbasedir/yourpackage/__init__.py导入 _version,如下所示:

__version__ = "unknown"
try:
    from _version import __version__
except ImportError:
    # We're running in a tree that doesn't have a _version.py, so we don't know what our version is.
    pass

An example of code that does this is the "pyutil" package that I maintain.执行此操作的代码示例是我维护的“pyutil”包。 (See PyPI or google search -- stackoverflow is disallowing me from including a hyperlink to it in this answer.) (请参阅 PyPI 或谷歌搜索——stackoverflow 不允许我在此答案中包含指向它的超链接。)

@pjeby is right that you shouldn't import your package from its own setup.py. @pjeby 是正确的,你不应该从它自己的 setup.py 中导入你的包。 That will work when you test it by creating a new Python interpreter and executing setup.py in it first thing: python setup.py , but there are cases when it won't work.当您通过创建一个新的 Python 解释器并首先在其中执行 setup.py 来测试它时,这将起作用: python setup.py ,但在某些情况下它不起作用。 That's because import youpackage doesn't mean to read the current working directory for a directory named "yourpackage", it means to look in the current sys.modules for a key "yourpackage" and then to do various things if it isn't there.那是因为import youpackage并不意味着读取名为“yourpackage”的目录的当前工作目录,它意味着在当前的sys.modules中查找键“yourpackage”,然后在它不存在时执行各种操作. So it always works when you do python setup.py because you have a fresh, empty sys.modules , but this doesn't work in general.因此,当您执行python setup.py时它总是有效,因为您有一个新的、空的sys.modules ,但这通常不起作用。

For example, what if py2exe is executing your setup.py as part of the process of packaging up an application?例如,如果 py2exe 在打包应用程序的过程中执行 setup.py 怎么办? I've seen a case like this where py2exe would put the wrong version number on a package because the package was getting its version number from import myownthing in its setup.py, but a different version of that package had previously been imported during the py2exe run.我见过这样的情况,其中 py2exe 会将错误的版本号放在一个包上,因为该包是从 setup.py 中的import myownthing获取其版本号,但之前在 py2exe 期间导入了该包的不同版本跑。 Likewise, what if setuptools, easy_install, distribute, or distutils2 is trying to build your package as part of a process of installing a different package that depends on yours?同样,如果 setuptools、easy_install、distribute 或 distutils2 在安装依赖于您的不同软件包的过程中尝试构建您的软件包,该怎么办? Then whether your package is importable at the time that its setup.py is being evaluated, or whether there is already a version of your package that has been imported during this Python interpreter's life, or whether importing your package requires other packages to be installed first, or has side-effects, can change the results.然后在评估 setup.py 时您的包是否可导入,或者在 Python 解释器的生命周期中是否已经导入了您的包的版本,或者导入您的包是否需要先安装其他包,或者有副作用,可以改变结果。 I've had several struggles with trying to re-use Python packages which caused problems for tools like py2exe and setuptools because their setup.py imports the package itself in order to find its version number.我在尝试重用 Python 包时遇到了几次困难,这导致 py2exe 和 setuptools 等工具出现问题,因为它们的 setup.py 会导入包本身以查找其版本号。

By the way, this technique plays nicely with tools to automatically create the yourpackage/_version.py file for you, for example by reading your revision control history and writing out a version number based on the most recent tag in revision control history.顺便说一句,这种技术可以很好地与自动为您创建yourpackage/_version.py文件的工具配合使用,例如通过读取您的修订控制历史记录并根据修订控制历史记录中的最新标签写出版本号。 Here is a tool that does that for darcs: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst and here is a code snippet which does the same thing for git: http://github.com/warner/python-ecdsa/blob/0ed702a9d4057ecf33eea969b8cf280eaccd89a1/setup.py#L34这是对 darcs 执行此操作的工具:http: //tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst ,这是对 git 执行相同操作的代码片段: http://github .com/warner/python-ecdsa/blob/0ed702a9d4057ecf33eea969b8cf280eaccd89a1/setup.py#L34

We wanted to put the meta information about our package pypackagery in __init__.py , but could not since it has third-party dependencies as PJ Eby already pointed out (see his answer and the warning regarding the race condition).我们想将关于我们的包pypackagery的元信息放在__init__.py中,但不能,因为它具有第三方依赖项,正如 PJ Eby 已经指出的那样(请参阅他的回答和有关竞争条件的警告)。

We solved it by creating a separate module pypackagery_meta.py that contains only the meta information:我们通过创建一个包含元信息的单独模块pypackagery_meta.py解决了这个问题:

"""Define meta information about pypackagery package."""

__title__ = 'pypackagery'
__description__ = ('Package a subset of a monorepo and '
                   'determine the dependent packages.')
__url__ = 'https://github.com/Parquery/pypackagery'
__version__ = '1.0.0'
__author__ = 'Marko Ristin'
__author_email__ = 'marko.ristin@gmail.com'
__license__ = 'MIT'
__copyright__ = 'Copyright 2018 Parquery AG'

then imported the meta information in packagery/__init__.py :然后在packagery/__init__.py中导入元信息:

# ...

from pypackagery_meta import __title__, __description__, __url__, \
    __version__, __author__, __author_email__, \
    __license__, __copyright__

# ...

and finally used it in setup.py :最后在setup.py中使用它:

import pypackagery_meta

setup(
    name=pypackagery_meta.__title__,
    version=pypackagery_meta.__version__,
    description=pypackagery_meta.__description__,
    long_description=long_description,
    url=pypackagery_meta.__url__,
    author=pypackagery_meta.__author__,
    author_email=pypackagery_meta.__author_email__,
    # ...
    py_modules=['packagery', 'pypackagery_meta'],
 )

You must include pypackagery_meta into your package with py_modules setup argument.您必须使用py_modules设置参数将pypackagery_meta包含到您的包中。 Otherwise, you can not import it upon installation since the packaged distribution would lack it.否则,您无法在安装时导入它,因为打包发行版会缺少它。

This should also work, using regular expressions and depending on the metadata fields to have a format like this:这也应该起作用,使用正则表达式并根据元数据字段具有如下格式:

__fieldname__ = 'value'

Use the following at the beginning of your setup.py:在 setup.py 的开头使用以下内容:

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

After that, you can use the metadata in your script like this:之后,您可以像这样在脚本中使用元数据:

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

Simple and straight, create a file called source/package_name/version.py with the following contents:简单直接,创建一个名为source/package_name/version.py的文件,其内容如下:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
__version__ = "2.6.9"

Then, on your file source/package_name/__init__.py , you import the version for other people to use:然后,在您的文件source/package_name/__init__.py ,导入版本供其他人使用:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from .version import __version__

Now, you can put this on setup.py现在,你可以把它放在setup.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'
    version_file = open( filepath )
    __version__ ,= re.findall( '__version__ = "(.*)"', version_file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

finally:
    version_file.close()

Tested this with Python 2.7 , 3.3 , 3.4 , 3.5 , 3.6 and 3.7 on Linux, Windows and Mac OS.在 Linux、Windows 和 Mac OS 3.7 3.4 2.7 3.3 3.5 3.6 I used on my package which has Integration and Unit Tests for all theses platforms.我在我的包上使用了所有这些平台的集成和单元测试。 You can see the results from .travis.yml and appveyor.yml here:您可以在此处查看.travis.ymlappveyor.yml的结果:

  1. https://travis-ci.org/evandrocoan/debugtools/builds/527110800 https://travis-ci.org/evandrocoan/debugtools/builds/527110800
  2. https://ci.appveyor.com/project/evandrocoan/pythondebugtools/builds/24245446 https://ci.appveyor.com/project/evandrocoan/pythondebugtools/builds/24245446

An alternate version is using context manager:另一个版本正在使用上下文管理器:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'

    with open( filepath ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

You can also be using the codecs module to handle unicode errors both on Python 2.7 and 3.6您还可以使用codecs模块来处理 Python 2.73.6上的 unicode 错误

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs

try:
    filepath = 'source/package_name/version.py'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

If you are writing a Python module 100% in C/C++ using Python C Extensions, you can do the same thing, but using C/C++ instead of Python.如果您使用 Python C 扩展 100% 用 C/C++ 编写 Python 模块,您可以做同样的事情,但使用 C/C++ 而不是 Python。

On this case, create the following setup.py :在这种情况下,创建以下setup.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs
from setuptools import setup, Extension

try:
    filepath = 'source/version.h'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

setup(
        name = 'package_name',
        version = __version__,

        package_data = {
                '': [ '**.txt', '**.md', '**.py', '**.h', '**.hpp', '**.c', '**.cpp' ],
            },

        ext_modules = [
            Extension(
                name = 'package_name',
                sources = [
                    'source/file.cpp',
                ],
                include_dirs = ['source'],
            )
        ],
    )

Which reads the version from the file version.h :从文件version.h中读取版本:

const char* __version__ = "1.0.12";

But, do not forget to create the MANIFEST.in to include the version.h file:但是,不要忘记创建MANIFEST.in以包含version.h文件:

include README.md
include LICENSE.txt

recursive-include source *.h

And it is integrated into the main application with:它集成到主应用程序中:

#include <Python.h>
#include "version.h"

// create the module
PyMODINIT_FUNC PyInit_package_name(void)
{
    PyObject* thismodule;
    ...

    // https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
    PyObject_SetAttrString( thismodule, "__version__", Py_BuildValue( "s", __version__ ) );

    ...
}

References:参考:

  1. python open file error python打开文件错误
  2. Define a global in a Python module from a C API 从 C API 在 Python 模块中定义全局变量
  3. How to include package data with setuptools/distribute? 如何在 setuptools/distribute 中包含包数据?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4 https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. How to use setuptools packages and ext_modules with the same name? 如何使用同名的 setuptools 包和 ext_modules?
  6. Is it possible to include subdirectories using dist utils (setup.py) as part of package data? 是否可以使用 dist utils (setup.py) 包含子目录作为包数据的一部分?

There's a thousand ways to skin a cat -- here's mine:给猫剥皮有一千种方法——这是我的:

# 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.")

To avoid importing a file (and thus executing its code) one could parse it and recover the version attribute from the syntax tree:为了避免导入文件(并因此执行其代码),可以对其进行解析并从语法树中恢复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 = v.s
                    break
                if isinstance(v, ast.Tuple):
                    r = []
                    for e in v.elts:
                        if isinstance(e, ast.Str):
                            r.append(e.s)
                        elif isinstance(e, ast.Num):
                            r.append(str(e.n))
                    version = '.'.join(r)
                    break

This code tries to find the __version__ or VERSION assignment at the top level of the module return is string value.此代码尝试在模块返回的顶层找到__version__VERSION赋值是字符串值。 The right side can be either a string or a tuple.右侧可以是字符串或元组。

Cleaning up https://stackoverflow.com/a/12413800 from @gringo-suave:从@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

Now this is gross and needs some refining (there may even be an uncovered member call in pkg_resources that I missed), but I simply do not see why this doesn't work, nor why no one has suggested it to date (Googling around has not turned this up)...note that this is Python 2.x, and would require requiring pkg_resources (sigh):现在这很严重,需要一些改进(我什至错过了 pkg_resources 中未发现的成员调用),但我根本不明白为什么这不起作用,也不知道为什么迄今为止没有人建议它(谷歌搜索有没有打开)...请注意,这是 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

deploy package to server and file naming convention for indices packages :将包部署到服务器和索引包的文件命名约定:

example for pip dynamic version conversion: pip 动态版本转换示例:

  • win:赢:

    • test_pkg-1.0.0-cp36-cp36m-win_amd64.whl test_pkg-1.0.0-cp36-cp36m-win_amd64.whl
    • test_pkg-1.0.0-py3.6-win-amd64.egg test_pkg-1.0.0-py3.6-win-amd64.egg
  • mac:苹果电脑:

    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.egg test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.egg
    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.whl test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.whl
  • linux: linux:
    • test_pkg-1.0.0-cp36-cp36m-linux_x86_64.whl test_pkg-1.0.0-cp36-cp36m-linux_x86_64.whl
from setuptools_scm import get_version

def _get_version():

     dev_version = str(".".join(map(str, str(get_version()).split("+")[0]\
            .split('.')[:-1])))

    return dev_version

Find the sample setup.py calls the dynamic pip version matching from git commit找到示例 setup.py 调用来自 git commit 的动态 pip 版本匹配

setup(
    version=_get_version(),
    name=NAME,
    description=DESCRIPTION,
    long_description=LONG_DESCRIPTION,
    classifiers=CLASSIFIERS,

# add few more for wheel wheel package ...conversion

)

I am using an environment variable as below我正在使用如下环境变量

VERSION=0.0.0 python setup.py sdist bdist_wheel版本=0.0.0 python setup.py sdist bdist_wheel

In setup.py在 setup.py


import os

setup(
    version=os.environ['VERSION'],
    ...
)

For consistency check with packer version, I am using below script.为了与打包程序版本进行一致性检查,我使用下面的脚本。

PKG_VERSION=`python -c "import pkg; print(pkg.__version__)"`
if [ $PKG_VERSION == $VERSION ]; then
    python setup.py sdist bdist_wheel
else
    echo "Package version differs from set env variable"
fi

For what is worth, I wrote getversion to solve this issue for one of our projects' needs.值得一提的是,我写了getversion来解决这个问题,以满足我们项目的一个需求。 It relies on a sequence of PEP-compliant strategies to return the version for a module, and adds some strategies for development mode (git scm).它依赖于一系列符合 PEP 的策略来返回模块的版本,并为开发模式 (git scm) 添加了一些策略。

Example:例子:

from getversion import get_module_version

# Get the version of an imported module
from xml import dom
version, details = get_module_version(dom)
print(version)

Yields产量

3.7.3.final.0

Why was this version found ?为什么找到这个版本? You can understand it from the details :你可以从details上理解:

> print(details)
Version '3.7.3.final.0' found for module 'xml.dom' by strategy 'get_builtin_module_version', after the following failed attempts:
 - Attempts for module 'xml.dom':
   - <get_module_version_attr>: module 'xml.dom' has no attribute '__version__'
 - Attempts for module 'xml':
   - <get_module_version_attr>: module 'xml' has no attribute '__version__'
   - <get_version_using_pkgresources>: Invalid version number: None
   - <get_builtin_module_version>: SUCCESS: 3.7.3.final.0

More can be found in the documentation .更多可以在文档中找到。

You should include the version string in your package, eg as __version__ = 'major.minor.patch' in __init__.py , rather than retrieving the version defined in setup in your code.您应该在您的包中包含版本字符串,例如__version__ = 'major.minor.patch' in __init__.py ,而不是在您的代码中检索 setup 中定义的版本。

Then you can either import your package in setup.py and pass version = your_package.__version__ , or you can use version = attr: your_package.__version__ in setup.cfg.然后你可以在 setup.py 中导入你的包并传递version = your_package.__version__ ,或者你可以在 setup.cfg 中使用version = attr: your_package.__version__

I created the regex pattern to find version number from setup.cfg ?:[\s]+|[\s])?[=](?:[\s]+|[\s])?(.*)我创建了正则表达式模式以从 setup.cfg 中查找版本号?:[\s]+|[\s])?[=](?:[\s]+|[\s])?(.*)

import re
with open("setup.cfg", "r") as _file:
    data = _file.read()
print(re.findall(r"\nversion(?:[\s]+|[\s])?[=](?:[\s]+|[\s])?(.*)", data))
# -> ['1.1.0']

A lot of the other answers are outdated, I believe the standard way to get version information from an installed python 3.10 package is by using importlib.metadata as of PEP-0566许多其他答案都已过时,我相信从已安装的 python 3.10 package 获取版本信息的标准方法是使用PEP-0566起的 importlib.metadata

Official docs: https://docs.python.org/3.10/library/importlib.metadata.html官方文档: https://docs.python.org/3.10/library/importlib.metadata.html

from importlib.metadata import version
VERSION_NUM = version("InstalledPackageName")

This is simple, clean, and no fuss.这很简单,干净,没有大惊小怪。

This won't work if you are doing something weird in a script that runs during package installation, but if all you are doing is getting the version number for a version check to show the user in through a CLI --help command, about box, or anything else where your package is already installed and just needs the installed version number this seems like the best solution to me.如果您在 package 安装期间运行的脚本中做一些奇怪的事情,这将不起作用,但是如果您所做的只是获取版本号以通过 CLI --help 命令向用户显示版本检查,关于框, 或者其他已经安装了 package 并且只需要安装的版本号的东西,这对我来说似乎是最好的解决方案。

You can add this code to your __init__.py :您可以将此代码添加到您的__init__.py

VERSION = (0, 3, 0)


def get_version():
    """Return the VERSION as a string.
    For example, if `VERSION == (0, 10, 7)`, return '0.10.7'.
    """
    return ".".join(map(str, VERSION))


__version__ = get_version()

And add this to the setup.py :并将其添加到setup.py中:

def get_version(version_tuple):
    """Return the version tuple as a string, e.g. for (0, 10, 7),
    return '0.10.7'.
    """
    return ".".join(map(str, version_tuple))


init = os.path.join(os.path.dirname(__file__), "your_library", "__init__.py")
version_line = list(filter(lambda line: line.startswith("VERSION"), open(init)))[0]
VERSION = get_version(eval(version_line.split("=")[-1]))

And finally, you can add the version=VERSION, line to the setup :最后,您可以将version=VERSION,行添加到setup中:

setup(
    name="your_library",
    version=VERSION,
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM