回答:
実行時にパッケージ内からバージョンを取得するには(質問が実際に尋ねているように見えます)、次を使用できます。
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__
することができます。
すべてのPythonバージョン、およびバージョン文字列へのアクセスが必要になる可能性のある非Python言語でさえも機能する、はるかに簡単な方法が必要な場合:
バージョン文字列を、たとえばという名前のプレーンテキストファイルの唯一のコンテンツとして保存し、VERSION
中にそのファイルを読み取りますsetup.py
。
version_file = open(os.path.join(mypackage_root_dir, 'VERSION'))
version = version_file.read().strip()
同じVERSION
ファイルは、Python以外のプログラムであっても、他のプログラムでもまったく同じように機能し、すべてのプログラムで1つの場所でバージョン文字列を変更するだけで済みます。
ちなみに、ここの別の回答で提案されているように、setup.pyからパッケージをインポートしないでください。これは、(パッケージの依存関係がすでにインストールされているため)機能すると思われますが、パッケージの新しいユーザーに大混乱をもたらします。 、依存関係を最初に手動でインストールしないとパッケージをインストールできないためです。
execfile
本当にうまく動作します...しかし、(悲しげに)はPython 3では動作しません
with open('mypackage/version.py') as f: exec(f.read())
ではの代わりに使用しexecfile('mypackage/version.py')
ます。(stackoverflow.com/a/437857/647002から)
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
mymodule
セットアップ中のインポートあなたの場合はsetup.py
輸入はmymodule
その後、セットアップ中にあなたが最も可能性の高いになるだろうImportError
。これは、パッケージに依存関係がある場合の非常に一般的なエラーです。パッケージにビルトイン以外の依存関係がない場合は、安全です。ただし、これは良い方法ではありません。その理由は、それが将来性がないということです。明日、あなたのコードは他の依存関係を消費する必要があると言います。
__version__
どこですか?ハードコード化__version__
するsetup.py
と、モジュールに同梱されるバージョンと一致しない場合があります。一貫性を保つために、1か所に置き、必要なときに同じ場所から読み取ることができます。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__
が何らかの方法で壊れている場合、インポートも壊れます。Pythonは、必要なステートメントのみを解釈する方法を知りません。@pjebyは正しい:モジュールが他のモジュールをインポートする必要がある場合、それらはまだインストールされていない可能性があり、混乱します。この手法は、インポートが他のインポートの長いチェーンを引き起こさないように注意する場合に機能します。
pip install <packagename>
、次のエラーが発生しますImportError: No module named <packagename>
。パッケージがまだインストールされていない環境では、このようなsetup.pyファイルを実行できないことを読者に警告してください!
私はこれらの答えに満足していませんでした... 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つの値だけではありません。
ast.get_docstring()
、いくつかを使用することもできます。同期を維持するためのもう1つのこと.split('\n')[0].strip()
description
たとえばyourbasedir / yourpackage / _version.pyのように、ソースツリーにファイルを作成します。次のように、そのファイルにコードを1行だけ含めます。
__version__ = "1.1.0-r4704"
次に、setup.pyでそのファイルを開き、次のようにバージョン番号を解析します。
verstr = "不明" 試してください: verstrline = open( 'yourpackage / _version.py'、 "rt")。read() EnvironmentErrorを除く: pass#さて、バージョンファイルはありません。 そうしないと: VSRE = r "^ __ version__ = ['\"]([^' \ "] *)['\"] " mo = re.search(VSRE、バーストライン、re.M) もしも: verstr = mo.group(1) そうしないと: RuntimeError( "yourpackage / _version.pyでバージョンが見つかりません")を発生させます
最後に、yourbasedir/yourpackage/__init__.py
import _versionでは次のようにします。
__version__ = "不明" 試してください: from _version import __version__ ImportErrorを除く: #_version.pyがないツリーで実行しているため、バージョンがわかりません。 パス
これを行うコードの例は、私が保守している「pyutil」パッケージです。(PyPIまたはgoogle searchを参照-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']
ファイルのインポート(およびそのコードの実行)を回避するには、ファイルを解析して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.")
@ 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
これは全体的であり、いくつかの調整が必要です(私が見逃した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
私たちは、パッケージに関するメタ情報を入れたかった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
使用してパッケージに含める必要があります。そうしないと、パッケージ化されたディストリビューションにはないため、インストール時にインポートできません。
シンプルでわかりやすい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
ここに:
別のバージョンでは、コンテキストマネージャを使用しています。
#!/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 2.7
とUnicodeの両方でUnicodeエラーを処理することもできます。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 Extensionsを使用してC / C ++で100%Pythonモジュールを作成している場合、同じことを実行できますが、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__ ) );
...
}
参照:
パッケージをサーバーに展開し、インデックスパッケージのファイル命名規則:
pip動的バージョン変換の例:
勝つ:
マック:
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
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
)
以下のように環境変数を使用しています
VERSION = 0.0.0 python setup.py sdist bdist_wheel
setup.py
import os
setup(
version=os.environ['VERSION'],
...
)
パッカー版との整合性チェックのため、以下のスクリプトを使用しています。
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