パッケージ内の setup.py (setuptools) で定義されているバージョンを取得するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/2058802

  •  20-09-2019
  •  | 
  •  

質問

で定義されているバージョンを取得するにはどうすればよいですか setup.py 私のパッケージから( --version, 、または他の目的)?

役に立ちましたか?

解決

インストール済みのディストリビューションのバージョン文字列に照会

実行時にパッケージ内部からのバージョンを取得するには(あなたの質問は、実際に尋ねると思われるもの)は、使用することができます:

import pkg_resources  # part of setuptools
version = pkg_resources.require("MyProject")[0].version
使用のための

ストアバージョン文字列は、インストール中に

あなたは(他の回答著者はここにあなたが求めていたと思っているように見える何のように見える)他の方法」ラウンドに行きたい場合は、別のファイルにバージョン文字列を入れて、setup.pyで、そのファイルの内容を読み取ります。

それはsetup.py名前空間に__version__を設定するように

あなたは、その後、execfile('mypackage/version.py')を使用してsetup.pyからそれを読んで、__version__ラインを使ってパッケージにversion.pyを作ることができます。

あなたがバージョン文字列へのアクセスを必要とする可能性のあるすべてのPythonのバージョン、さらには非Pythonの言語で動作しますはるかに簡単な方法をしたい場合:

などという名前のプレーンテキストファイルの唯一の内容として、バージョン文字列を保存VERSION、およびsetup.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をハードコードした場合、

それはあなたがあなたのモジュールに出荷うバージョンと一致しない場合があります。一貫性を保つためには、一つの場所にそれを入れて、あなたがそれを必要なときに同じ場所からそれを読んでいました。あなたは問題#1を得ることができimport使用します。

ソリューション:アラカルトsetuptools

あなたは、openの組み合わせを使用execと変数を追加するexecのための辞書を提供します。

# setup.py
from setuptools import setup, find_packages
from distutils.util import convert_path

main_ns = {}
ver_path = convert_path('mymodule/version.py')
with open(ver_path) as ver_file:
    exec(ver_file.read(), main_ns)

setup(...,
    version=main_ns['__version__'],
    ...)

そしてmymodule/version.pyでバージョンを公開します:

__version__ = 'some.semantic.version'

この方法では、バージョンがモジュールに同梱されている、とあなたは依存関係が欠落したモジュールをインポートしようとしてセットアップ中に問題を持っていない(まだインストールされている)。

最高の技術がそこからsetup.pyにインポートした後、お使いの製品コードで__version__を定義することです。これは、あなたが実行中のモジュールを読み込み、それを定義するための唯一の場所を持つことができる値が得られます。

setup.pyの値がインストールされていない、とsetup.pyは、インストール後に固執しません。

私はcoverage.pyに(例えば)でします:

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

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

UPDATE(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 にあります。このファイルには、次のように 1 行のコードのみが含まれるようにします。

__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 または Google 検索を参照してください -- stackoverflow により、この回答にハイパーリンクを含めることができなくなりました。)

@pjeby は、パッケージを独自の setup.py からインポートすべきではないという意見は正しいです。これは、新しい Python インタープリターを作成し、最初にその中で setup.py を実行してテストすると機能します。 python setup.py, しかし、うまくいかないケースもあります。それは、 import youpackage これは、「yourpackage」という名前のディレクトリの現在の作業ディレクトリを読み取るという意味ではなく、現在のパッケージを調べることを意味します。 sys.modules キー「yourpackage」の場合、それが存在しない場合はさまざまな処理を行います。したがって、実行すると常に機能します python setup.py なぜならあなたには新鮮で空っぽのものがいるからです sys.modules, しかし、これは一般的には機能しません。

たとえば、アプリケーションをパッケージ化するプロセスの一部として py2exe が setup.py を実行している場合はどうなるでしょうか?パッケージのバージョン番号が import myownthing しかし、そのパッケージの別のバージョンが py2exe の実行中に以前にインポートされていました。同様に、setuptools、easy_install、distribute、または distutils2 が、あなたのパッケージに依存する別のパッケージをインストールするプロセスの一部としてあなたのパッケージをビルドしようとした場合はどうなるでしょうか?次に、setup.py の評価時にパッケージがインポート可能かどうか、この Python インタープリターの有効期間中にインポートされたパッケージのバージョンがすでに存在するかどうか、またはパッケージをインポートするには最初に他のパッケージをインストールする必要があるかどうかを確認します。 、または副作用がある場合、結果が変わる可能性があります。Python パッケージを再利用しようとすると、setup.py がバージョン番号を見つけるためにパッケージ自体をインポートするため、py2exe や setuptools などのツールで問題が発生するという問題が何度かありました。

ちなみに、このテクニックは、 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に持っているものは何でも依存関係に問題が発生することはありません/ __ __ INIT。PY

ファイルをインポートする(したがって、そのコードを実行する)を回避するために1がそれを解析し、構文木から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で覆われていないメンバーの呼び出しがあるかもしれない)が、これは動作しませんなぜ私は単に表示されていない、また、なぜ誰も(これまでにそれを示唆していません周りグーグルでは(これは、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()

これをPythonでテストしました 2.7, 3.3, 3.4, 3.5, 3.6 そして 3.7 Linux、Windows、Mac OS 上で。私は、これらすべてのプラットフォームの統合テストと単体テストを含むパッケージを使用しました。結果はから見ることができます .travis.yml そして appveyor.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 Python の両方で Unicode エラーを処理するモジュール 2.7 そして 3.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 C 拡張機能を使用して 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. Pythonでファイルを開くエラー
  2. C API から Python モジュールでグローバルを定義する
  3. setuptools/distribute にパッケージ データを含めるにはどうすればよいですか?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. 同じ名前の setuptools パッケージと ext_modules を使用するにはどうすればよいですか?
  6. dist utils (setup.py) を使用してパッケージ データの一部としてサブディレクトリを含めることはできますか?
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top