setup.py
で定義されているバージョンをパッケージから取得するにはどうすればよいですか (--version
またはその他の目的で)?
17 に答える
すでにインストールされているディストリビューションのバージョン文字列を調べる
実行時にパッケージ内からバージョンを取得するには (質問が実際に求めているように見えます)、次を使用できます。
import pkg_resources # part of setuptools
version = pkg_resources.require("MyProject")[0].version
インストール時に使用するバージョン文字列を保存する
逆に行きたい場合は(これは、ここにいる他の回答者があなたが求めていると考えていたようです)、バージョン文字列を別のファイルに入れ、そのファイルの内容をsetup.py
.
パッケージ内に version.py を 1__version__
行で作成し、それを を使用して setup.py から読み取ると、setup.py 名前空間execfile('mypackage/version.py')
に設定されます。__version__
インストール中の競合状態に関する警告
ちなみに、ここの別の回答で提案されているように、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 つの場所に置き、必要なときに同じ場所から読み取ります。youを使用import
すると、問題 #1 が発生する可能性があります。
解決策:アラsetuptools
の組み合わせを使用し、open
変数を追加するexec
ために dict を提供します。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にインポートすることです。これにより、実行中のモジュールで読み取ることができる値が得られ、それを定義する場所は1つだけです。
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 は使い捨てモジュールのようなものなので、多少見栄えが悪くても問題ありません。
更新: おもしろいことに、私はここ数年、これをやめて、パッケージ内の別のファイルを使用し始めましたmeta.py
。頻繁に変更したいメタデータをたくさん入れています。したがって、1 つの値だけではありません。
ソース ツリー (例: yourbasedir/yourpackage/_version.py ) にファイルを作成します。次のように、そのファイルに 1 行のコードだけを含めます。
__version__ = "1.1.0-r4704"
次に、setup.py でそのファイルを開き、次のようにバージョン番号を解析します。
verstr =「不明」 試す: verstrline = open('yourpackage/_version.py', "rt").read() 環境エラーを除く: pass # さて、バージョンファイルはありません。 そうしないと: VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" mo = re.search(VSRE, verstrline, re.M) モの場合: verstr = mo.group(1) そうしないと: 発生する RuntimeError("パッケージ/_version.py でバージョンが見つかりません")
最後に、yourbasedir/yourpackage/__init__.py
次のように import _version で:
__バージョン__ = 「不明」 試す: from _version import __version__ ImportError を除く: # _version.py を持たないツリーで実行しているため、バージョンがわかりません。 合格
これを行うコードの例は、私が管理している「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 を実行している場合はどうなるでしょうか? パッケージがからバージョン番号を取得していたため、py2exeがパッケージに間違ったバージョン番号を付けるこのようなケースを見てきましたimport myownthing
そのsetup.pyに含まれていますが、py2exeの実行中にそのパッケージの別のバージョンが以前にインポートされていました。同様に、setuptools、easy_install、distribute、または distutils2 が、あなたのパッケージに依存する別のパッケージをインストールするプロセスの一部として、あなたのパッケージをビルドしようとした場合はどうなるでしょうか? 次に、setup.py が評価されている時点でパッケージがインポート可能かどうか、またはこの Python インタープリターの有効期間中にインポートされたパッケージのバージョンが既に存在するかどうか、またはパッケージをインポートするために他のパッケージを最初にインストールする必要があるかどうか、または副作用があり、結果が変わる可能性があります。setup.py はバージョン番号を見つけるためにパッケージ自体をインポートするため、py2exe や setuptools などのツールで問題を引き起こした Python パッケージを再利用しようとすると、いくつかの苦労がありました。
ちなみに、この手法は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']
@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
ファイルのインポート (およびそのコードの実行) を回避するには、ファイルを解析して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__
or割り当てを見つけようとします。戻り値は文字列値です。VERSION
右側は文字列またはタプルのいずれかです。
価値があるのは、私たちのプロジェクトのニーズの 1 つについて、この問題を解決するためにgetversionを作成したことです。モジュールのバージョンを返す一連の PEP 準拠の戦略に依存し、開発モード (git scm) のいくつかの戦略を追加します。
例:
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)
収量
3.7.3.final.0
なぜこのバージョンが見つかったのですか? あなたはそれを理解することができます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
詳細については、ドキュメントを参照してください。