如何获得在安装中定义的版本。在我的包里?

时间:2022-01-10 20:10:00

How could I get the version defined in setup.py from my package (for --version, or other purposes)?

如何在安装中定义版本。我的包中的py(用于——版本或其他目的)?

11 个解决方案

#1


196  

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.

你可以制作一个版本。py在带有__version__行的包中,然后从setup中读取它。py使用execfile('mypackage/version.py'),以便在设置中设置__version__。py名称空间。

If you want a much simpler way that will work with all Python versions and even non-Python languages that may need access to the version string:

如果您想要更简单的方法,可以使用所有Python版本,甚至可能需要访问版本字符串的非Python语言:

Store the version string as the sole contents of a plain text file, named e.g. VERSION, and read that file during setup.py.

将版本字符串存储为纯文本文件(如version)的唯一内容,并在setup.py期间读取该文件。

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

The same VERSION file will then work exactly as well in any other program, even non-Python ones, and you only need to change the version string in one place for all programs.

同样的版本文件在任何其他程序,甚至非python的程序中都能正常工作,您只需要在一个地方为所有程序更改版本字符串。

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.

顺便说一下,不要从设置中导入包。py正如这里的另一个答案所建议的:它似乎对您有效(因为您已经安装了包的依赖项),但是它将对您的包的新用户造成严重的破坏,因为他们将无法在不首先手动安装依赖项的情况下安装您的包。

#2


22  

example study: 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:

然后想象一些常见场景,其中有依赖关系和设置。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

If your setup.py imports mymodule then during setup you would most likely get an 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.

如果您的设置。py导入mymodule,然后在安装过程中,您很可能会遇到一些麻烦。当您的包有依赖项时,这是一个非常常见的错误。如果您的包除了构建项之外没有其他依赖项,那么您可能是安全的;然而,这并不是一个好的实践。其原因是它不是未来的证明;假设明天您的代码需要使用一些其他依赖项。

problem #2: where's my __version__ ?

If you hardcode __version__ in setup.py then it may not match the version that you would ship in your module. 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.

如果你在设置中使用了__version__。然后,它可能与模块中提供的版本不匹配。为了保持一致,你会把它放在一个地方,在你需要的时候从同一个地方读。使用导入,您可能会得到问题#1。

solution: à la setuptools

You would use a combination of open, exec and provide a dict for exec to add variables:

您将使用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__'],
    ...)

And in mymodule/version.py expose the version:

并在mymodule里/版本。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).

通过这种方式,版本随模块一起发布,在安装过程中,您不会遇到导入缺少依赖项(尚未安装)的模块的问题。

#3


9  

The best technique is to define __version__ in your product code, then import it into setup.py from there. This gives you a value you can read in your running module, and have only one place to define it.

最好的技术是在产品代码中定义__version__,然后将其导入到setup中。py。这为您提供了一个在运行模块中可以读取的值,并且只有一个地方可以定义它。

The values in setup.py are not installed, and setup.py doesn't stick around after installation.

中的值设置。未安装py和安装。py安装后不会逗留。

What I did (for example) in 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. 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.

更新(2017):覆盖。py不再导入自己以获取版本。导入您自己的代码可以使它无法安装,因为您的产品代码将尝试导入依赖项(还没有安装),因为安装。是py安装的。

#4


8  

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:

然后你可以确认这个设置。py知道您刚才指定的版本:

% ./setup.py --version
1.4.4

#5


6  

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 a throwaway module so not an issue if it is a bit ugly.

设置。py是一个一次性的模块,所以如果它有点丑的话,也不是问题。

print `version`

#6


3  

Create a file in your source tree, e.g. in yourbasedir/yourpackage/_version.py . Let that file contain only a single line of code, like this:

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

__version__ = "1.1.0-r4704"

__version__ = " 1.1.0-r4704 "

Then in your setup.py, open that file and parse out the version number like this:

然后在你的设置。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. (See PyPI or google search -- * is disallowing me from including a hyperlink to it in this answer.)

我维护的“pyutil”包就是这样做的代码示例。(参见PyPI或谷歌搜索——*不允许我在这个答案中包含一个超链接。)

@pjeby is right that you shouldn't import your package from its own 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. 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. 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.

@pjeby是对的,您不应该从它自己的setup.py导入包。当您创建一个新的Python解释器并执行安装时,它将会工作。py在它的第一件事:python设置。py,但是有些情况下行不通。这是因为导入youpackage并不意味着要读取名为“yourpackage”的目录的当前工作目录,而是要查看当前sys。模块的关键“你的包”,然后做各种事情,如果它不存在。所以当你做python设置时它总是有效的。因为你有一个新的,空的系统。模块,但是这在一般情况下是行不通的。

For example, what if py2exe is executing your setup.py as part of the process of packaging up an application? 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. 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? 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. 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.

例如,如果py2exe正在执行您的设置。py作为打包应用程序过程的一部分吗?我看到过这样的情况,py2exe会将错误的版本号放在一个包中,因为包的设置中从导入myownthing得到了版本号。但是之前在py2exe运行期间导入了该包的另一个版本。同样,如果setuptools、easy_install、distribution或distutils2试图构建您的包,作为安装依赖于您自己的包的过程的一部分,该怎么办?然后,您的包是否在设置时是可导入的。正在对py进行评估,或者在这个Python解释器的生命周期中是否已经导入了包的一个版本,或者导入包是否需要先安装其他包,或者是否有副作用,都可以改变结果。我曾多次尝试重用Python包,这给py2exe和setuptools之类的工具带来了问题,因为它们的设置。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. 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

顺便说一下,这种技术与自动创建yourpackage/_version的工具配合得很好。例如,通过阅读您的修订控制历史,并根据修订控制历史上最新的标记写出一个版本号。下面是一个针对darcs的工具:http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst,下面是一个代码片段,它对git也有同样的作用

#7


2  

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']

#8


1  

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. The right side can be either a string or a tuple.

这段代码试图找到模块返回的顶层的__version__或版本分配为string值。右边可以是字符串也可以是元组。

#9


1  

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

#10


1  

Cleaning up https://*.com/a/12413800 from @gringo-suave:

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

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

#11


0  

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。需要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

#1


196  

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.

你可以制作一个版本。py在带有__version__行的包中,然后从setup中读取它。py使用execfile('mypackage/version.py'),以便在设置中设置__version__。py名称空间。

If you want a much simpler way that will work with all Python versions and even non-Python languages that may need access to the version string:

如果您想要更简单的方法,可以使用所有Python版本,甚至可能需要访问版本字符串的非Python语言:

Store the version string as the sole contents of a plain text file, named e.g. VERSION, and read that file during setup.py.

将版本字符串存储为纯文本文件(如version)的唯一内容,并在setup.py期间读取该文件。

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

The same VERSION file will then work exactly as well in any other program, even non-Python ones, and you only need to change the version string in one place for all programs.

同样的版本文件在任何其他程序,甚至非python的程序中都能正常工作,您只需要在一个地方为所有程序更改版本字符串。

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.

顺便说一下,不要从设置中导入包。py正如这里的另一个答案所建议的:它似乎对您有效(因为您已经安装了包的依赖项),但是它将对您的包的新用户造成严重的破坏,因为他们将无法在不首先手动安装依赖项的情况下安装您的包。

#2


22  

example study: 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:

然后想象一些常见场景,其中有依赖关系和设置。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

If your setup.py imports mymodule then during setup you would most likely get an 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.

如果您的设置。py导入mymodule,然后在安装过程中,您很可能会遇到一些麻烦。当您的包有依赖项时,这是一个非常常见的错误。如果您的包除了构建项之外没有其他依赖项,那么您可能是安全的;然而,这并不是一个好的实践。其原因是它不是未来的证明;假设明天您的代码需要使用一些其他依赖项。

problem #2: where's my __version__ ?

If you hardcode __version__ in setup.py then it may not match the version that you would ship in your module. 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.

如果你在设置中使用了__version__。然后,它可能与模块中提供的版本不匹配。为了保持一致,你会把它放在一个地方,在你需要的时候从同一个地方读。使用导入,您可能会得到问题#1。

solution: à la setuptools

You would use a combination of open, exec and provide a dict for exec to add variables:

您将使用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__'],
    ...)

And in mymodule/version.py expose the version:

并在mymodule里/版本。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).

通过这种方式,版本随模块一起发布,在安装过程中,您不会遇到导入缺少依赖项(尚未安装)的模块的问题。

#3


9  

The best technique is to define __version__ in your product code, then import it into setup.py from there. This gives you a value you can read in your running module, and have only one place to define it.

最好的技术是在产品代码中定义__version__,然后将其导入到setup中。py。这为您提供了一个在运行模块中可以读取的值,并且只有一个地方可以定义它。

The values in setup.py are not installed, and setup.py doesn't stick around after installation.

中的值设置。未安装py和安装。py安装后不会逗留。

What I did (for example) in 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. 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.

更新(2017):覆盖。py不再导入自己以获取版本。导入您自己的代码可以使它无法安装,因为您的产品代码将尝试导入依赖项(还没有安装),因为安装。是py安装的。

#4


8  

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:

然后你可以确认这个设置。py知道您刚才指定的版本:

% ./setup.py --version
1.4.4

#5


6  

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 a throwaway module so not an issue if it is a bit ugly.

设置。py是一个一次性的模块,所以如果它有点丑的话,也不是问题。

print `version`

#6


3  

Create a file in your source tree, e.g. in yourbasedir/yourpackage/_version.py . Let that file contain only a single line of code, like this:

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

__version__ = "1.1.0-r4704"

__version__ = " 1.1.0-r4704 "

Then in your setup.py, open that file and parse out the version number like this:

然后在你的设置。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. (See PyPI or google search -- * is disallowing me from including a hyperlink to it in this answer.)

我维护的“pyutil”包就是这样做的代码示例。(参见PyPI或谷歌搜索——*不允许我在这个答案中包含一个超链接。)

@pjeby is right that you shouldn't import your package from its own 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. 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. 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.

@pjeby是对的,您不应该从它自己的setup.py导入包。当您创建一个新的Python解释器并执行安装时,它将会工作。py在它的第一件事:python设置。py,但是有些情况下行不通。这是因为导入youpackage并不意味着要读取名为“yourpackage”的目录的当前工作目录,而是要查看当前sys。模块的关键“你的包”,然后做各种事情,如果它不存在。所以当你做python设置时它总是有效的。因为你有一个新的,空的系统。模块,但是这在一般情况下是行不通的。

For example, what if py2exe is executing your setup.py as part of the process of packaging up an application? 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. 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? 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. 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.

例如,如果py2exe正在执行您的设置。py作为打包应用程序过程的一部分吗?我看到过这样的情况,py2exe会将错误的版本号放在一个包中,因为包的设置中从导入myownthing得到了版本号。但是之前在py2exe运行期间导入了该包的另一个版本。同样,如果setuptools、easy_install、distribution或distutils2试图构建您的包,作为安装依赖于您自己的包的过程的一部分,该怎么办?然后,您的包是否在设置时是可导入的。正在对py进行评估,或者在这个Python解释器的生命周期中是否已经导入了包的一个版本,或者导入包是否需要先安装其他包,或者是否有副作用,都可以改变结果。我曾多次尝试重用Python包,这给py2exe和setuptools之类的工具带来了问题,因为它们的设置。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. 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

顺便说一下,这种技术与自动创建yourpackage/_version的工具配合得很好。例如,通过阅读您的修订控制历史,并根据修订控制历史上最新的标记写出一个版本号。下面是一个针对darcs的工具:http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst,下面是一个代码片段,它对git也有同样的作用

#7


2  

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']

#8


1  

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. The right side can be either a string or a tuple.

这段代码试图找到模块返回的顶层的__version__或版本分配为string值。右边可以是字符串也可以是元组。

#9


1  

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

#10


1  

Cleaning up https://*.com/a/12413800 from @gringo-suave:

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

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

#11


0  

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。需要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