我怎样才能获得定义的版本 setup.py 从我的包裹中(对于 --version, ,或其他目的)?

有帮助吗?

解决方案

讯问已经安装的分布的版本字符串

要检索您的包内运行时版本(显示你的问题实际上被询问),您可以使用:

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

存储版本字符串,在使用过程中安装

如果你想要去的其他方式“轮(这似乎是什么出现在这里的其他答案的作者已经以为你是问),把版本字符串在一个单独的文件和读取setup.py该文件的内容。

您可以做一个version.py在你的包有__version__线,然后用execfile('mypackage/version.py')从setup.py读它,使它集__version__在setup.py命名空间。

如果你想有一个,将与可能需要访问版本字符串的所有Python版本,甚至非Python这样的语言的工作更简单的方式:

存储的版本的字符串作为一个纯文本文件的唯一内容,例如命名VERSIONsetup.py期间读取该文件。

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

同样VERSION文件将工作完全以及其他任何程序,甚至非的Python的人,而你只需要改变的版本字符串在一个地方的所有程序。

警告关于种族条件期间安装

顺便说一句,不要从你的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__

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

的解决方案:点菜setuptools

您会使用openexec的组合,并且提供一个字典用于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'

这样,该版本附带的模块,并设置要导入已缺少的依赖关系模块中没有问题(尚未安装)。

最好的方法是在你的产品代码来定义__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就是安装它们。

您的问题有点模糊,但我想你问的是如何规定的。

您需要定义像这样__version__

__version__ = '1.4.4'

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

% ./setup.py --version
1.4.4

我很不满意这些答案......不想要求setuptools的,也不是要针对单一变量的整体单独的模块,所以我想到了这些。

有关当您确定主模块处于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

如果您希望要格外小心,并使用一个真正的解析器:

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有点暴殄天物模块的这样不是一个问题,如果它是有点难看。

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

__version__ = "1.1.0-r4704"

然后在 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")

最后,在 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

执行此操作的代码示例是我维护的“pyutil”包。(请参阅 PyPI 或谷歌搜索 - stackoverflow 不允许我在此答案中包含指向它的超链接。)

@pjeby 是对的,你不应该从它自己的 setup.py 导入你的包。当您通过创建一个新的 Python 解释器并首先在其中执行 setup.py 来测试它时,这将起作用: python setup.py, ,但有些情况下它不起作用。那是因为 import youpackage 并不意味着读取名为“yourpackage”的目录的当前工作目录,它意味着查找当前 sys.modules 寻找一个关键的“yourpackage”,然后如果它不存在则执行各种操作。所以当你这样做时它总是有效 python setup.py 因为你有一个新鲜的、空的 sys.modules, ,但这一般不起作用。

例如,如果 py2exe 正在执行 setup.py 作为打包应用程序过程的一部分,该怎么办?我见过这样的情况,其中 py2exe 会在包上放置错误的版本号,因为该包从以下位置获取其版本号 import myownthing 在其 setup.py 中,但之前在 py2exe 运行期间导入了该包的不同版本。同样,如果 setuptools、easy_install、distribute 或 distutils2 尝试构建您的软件包作为安装依赖于您的软件包的过程的一部分,该怎么办?然后,您的包在其 setup.py 被评估时是否可导入,或者在此 Python 解释器的生命周期中是否已经导入了您的包的版本,或者导入您的包是否需要先安装其他包,或有副作用,可以改变结果。我在尝试重用 Python 包时遇到了一些困难,这给 py2exe 和 setuptools 等工具带来了问题,因为它们的 setup.py 导入包本身以查找其版本号。

顺便说一句,这项技术与自动创建 yourpackage/_version.py 为您创建文件,例如,通过读取修订控制历史记录并根据修订控制历史记录中的最新标签写出版本号。这是一个针对 darcs 执行此操作的工具: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst 这是一个对 git 执行相同操作的代码片段: http://github.com/warner/python-ecdsa/blob/0ed702a9d4057ecf33eea969b8cf280eaccd89a1/setup.py#L34

此也应该工作,使用正则表达式,并根据所述元数据字段有一个这样的格式:

__fieldname__ = 'value'

使用下面你setup.py的开头:

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

在这之后,你可以使用元数据在你的脚本是这样的:

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

通过这样的结构:

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

其中的 version.py 包含:

__version__ = 'version_string'

您可以做到这一点的 setup.py

import sys

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

from version import __version__

这会不会导致你在你的mymodule中的任何相关性的任何问题/ __ __初始化。PY

要避免导入一个文件(并因此执行其代码)人们可以分析它并恢复从语法树的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

此代码试图找到在模块返回的顶层的__version__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.")

这@清理 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

我们希望把我们在pypackagery__init__.py元信息,但不能因为它有第三方的依赖关系PJ伊比已经指出(见他的回答和关于竞争条件的警告)。

我们通过创建包含一个单独的模块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'

然后导入的元信息中packagery/__init__.py

# ...

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

# ...

和最后用它在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'],
 )

您必须包括pypackagery_meta到您的包py_modules设置参数。否则,你不能导入它在安装时因为包装的分布会缺少它。

现在,这是严重和需要一些精炼(甚至有可能出现的,我错过了通过pkg_resources未覆盖的成员通话),但我只是不明白为什么这不工作,也不知道为什么没有人就建议日期(周围的Googling并没有把这个了)......注意,这是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

简单和直接,创建一个文件叫 source/package_name/version.py 与以下内容:

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

然后,在你的文件 source/package_name/__init__.py, 你进口的版本对其他人使用:

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

现在,你可以穿上这个 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()

测试这与蟒蛇 2.7, 3.3, 3.4, 3.5, 3.63.7 在Linux上,窗户和Mac OS。我用我的包,其中有整合和单元测试的所有论文的平台。你可以看到的结果 .travis.ymlappveyor.yml 在这里:

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

另一个版本使用方面管理:

#!/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 ) )

你也可以使用 codecs 模块来处理unicode错误两个月蟒蛇 2.73.6

#!/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 ) )

如果你正在写的一Python模块100%在C/C++使用Python C扩展,可以做同样的事情,但使用C++而不是蟒蛇。

在这种情况下,创建以下 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'],
            )
        ],
    )

哪读的版本的文件 version.h:

const char* __version__ = "1.0.12";

但是,不要忘记创建的 MANIFEST.in 包括 version.h 文件:

include README.md
include LICENSE.txt

recursive-include source *.h

它是纳入主要的应用程序:

#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__ ) );

    ...
}

参考文献:

  1. 蟒蛇打开文件中的错误
  2. 定义一个全球Python模块从C API
  3. 如何包括包装数据与设置工具/分发?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. 如何使用设置工具包和ext_modules用相同的名字吗?
  6. 这可能包括子目录的使用dist工具(setup.py)的一部分包的数据?
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top