標準的な方法を組み込みでバージョンのpythonですか。
質問
ある標準的な方法をバージョン文字列とのpythonパッケージをどう思いは次のうちどれでしょう?
import foo
print foo.version
思い方法で取得ることなくデータタイトルでhardcoding、オリジナルグッズを大文字列で指定され setup.py
ています。ュニケーションが見たい import __version__
私の foo/__init__.py
してい __version__.py
生成される setup.py
.
解決
質問への直接的な回答ではありませんが、__version__
ではなくversion
と命名することを検討する必要があります。
これはほぼ準標準です。標準ライブラリの多くのモジュールは<=>を使用し、これは多くのサードパーティモジュール 、それが準標準です。
通常、<=>は文字列ですが、フロートまたはタプルでもある場合があります。
編集:S.Lottが述べたように(ありがとう!)、 PEP 8 明示的に言っています:
バージョンの簿記
ソースファイルにSubversion、CVS、またはRCS crudが必要な場合、 次のようにしてください。
__version__ = "$Revision: 63990 $" # $Source$
これらの行は、モジュールのdocstringの後、前に含める必要があります 上下の空白行で区切られたその他のコード。
バージョン番号が PEP 440で説明されている形式に準拠していることも確認する必要があります。 ( PEP 386 この標準の以前のバージョン)
他のヒント
単一の_version.py
ファイルを<!> quot; once cannonical place <!> quot;として使用します。バージョン情報を保存するには:
-
__version__
属性を提供します。 -
標準のメタデータバージョンを提供します。したがって、
pkg_resources
またはパッケージメタデータを解析する他のツール(EGG-INFOおよび/またはPKG-INFO、PEP 0345)によって検出されます。 -
パッケージをビルドするときにパッケージ(または他の何か)をインポートしないため、状況によっては問題が発生する可能性があります。 (これが引き起こす可能性のある問題については、以下のコメントを参照してください。)
-
バージョン番号が書き留められる場所は1つしかないため、バージョン番号が変更されたときに変更する場所は1つだけであり、バージョンの不整合が発生する可能性は低くなります。
その仕組みは次のとおりです。<!> quot; one canonical place <!> quot;バージョン番号を保存するのは、<!> quot; _version.py <!> quotという名前の.pyファイルです。これは、たとえばmyniftyapp/_version.py
などのPythonパッケージにあります。このファイルはPythonモジュールですが、setup.pyはインポートしません! (これにより、機能3が無効になります。)その代わり、setup.pyは、このファイルの内容が次のような非常に単純であることを認識しています。
__version__ = "3.6.5"
そして、setup.pyがファイルを開き、次のようなコードで解析します:
import re
VERSIONFILE="myniftyapp/_version.py"
verstrline = open(VERSIONFILE, "rt").read()
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
mo = re.search(VSRE, verstrline, re.M)
if mo:
verstr = mo.group(1)
else:
raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
次に、setup.pyはその文字列を<!> quot; version <!> quotの値として渡します。 setup()
への引数、したがって機能2を満たします。
機能1を満たすために、パッケージを(セットアップ時ではなく、実行時に!)次のようにmyniftyapp/__init__.py
から_versionファイルをインポートできます:
from _version import __version__
この手法の例私は何年も使用しています。
この例のコードはもう少し複雑ですが、このコメントに書いた簡単な例は完全な実装になります。
このアプローチに何か問題がある場合は、お知らせください。
2017-05を書き直しました
Pythonコードを記述し、さまざまなパッケージを管理してから10年以上が経ちましたが、DIYはおそらく最良のアプローチではないという結論に達しました。
パッケージ内のバージョン管理にpbr
パッケージを使用し始めました。 SCMとしてgitを使用している場合、これは魔法のようにワークフローに適合し、数週間の作業を節約します(問題がどれほど複雑になるか驚くでしょう)。
今日 pbrは最も使用されているpythonパッケージで第11位にランクされており、このレベルに到達していません汚いトリックを含める:1つだけ:一般的なパッケージングの問題を非常に簡単な方法で修正する。
yourpackage/version.py
は、パッケージのメンテナンスの負担を増やすことができ、バージョン管理に限定されませんが、すべての利点を強制的に採用することはありません。
1回のコミットでpbrを採用する方法を確認するには、 swiching pbrへのパッケージ
おそらく、バージョンがリポジトリにまったく保存されていないことに気付くでしょう。 PBRはGitブランチとタグからそれを検出します。
pbrは<!> quot; compile <!> quot;を実行するため、gitリポジトリがない場合に何が起こるかを心配する必要はありません。アプリケーションをパッケージ化またはインストールするときにバージョンをキャッシュするため、gitにランタイム依存関係がありません。
旧ソリューション
これまでに見た中で最高の解決策であり、その理由も説明しています:
内部yourpackage/__init__.py
:
# Store the version here so:
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '0.12'
内部setup.py
:
from .version import __version__
内部<=>:
exec(open('yourpackage/version.py').read())
setup(
...
version=__version__,
...
より良いと思われる別のアプローチを知っている場合はお知らせください。
遅延 PEP 396(モジュールバージョン番号)に基づいて、これを行うための提案された方法。それは、従うべきモジュールのための(確かにオプションの)標準を、理論的根拠とともに説明しています。スニペットは次のとおりです。
3)モジュール(またはパッケージ)にバージョン番号が含まれている場合、そのバージョンは
__version__
属性で使用できる必要があります。4) 名前空間パッケージ内に存在するモジュールの場合、モジュールには<=>属性を含める必要があります。名前空間パッケージ自体には、独自の<=>属性を含めるべきではありません。
5) <=>属性の値は文字列である必要があります。
これは、と言えるのではないだろう遅いず代替前の答え:
__version_info__ = ('1', '2', '3')
__version__ = '.'.join(__version_info__)
(というかなり単純な変換の自動incrementing部のバージョン番号を文字列を使用 str()
.)
もちろん、僕が見たかのようなものがもたらす先版利用の場合 __version_info__
, なので、店舗としてのタプルのint;しかし、いった、ということとしていない場合がありますが演算などの追加または減算部分はバージョン番号の目的にも好奇心や自動incrementation(ともできたし、とても満足してい int()
や str()
使用できるかなりやすい)。(一方の可能性がありますので、誰かのコードを予想数値のタプルは文字列のタプルは、このような.)
これは、もちろん、私自身と私は喜んのように他人の入力を用いて数値のタプル.
としてsheziを思い起こさせ,(語彙の比較の数文字列が必ずしも同じ結果として直接数値比較、先頭にゼロを提供することが義務づける。そういう意味では、保存 __version_info__
(あうという)としてのタプルの整数値により効率的に版比較.
パッケージdirでJSONファイルを使用します。これはZookoの要件に適合しています。
内部pkg_dir/pkg_info.json
:
{"version": "0.1.0"}
内部setup.py
:
from distutils.core import setup
import json
with open('pkg_dir/pkg_info.json') as fp:
_info = json.load(fp)
setup(
version=_info['version'],
...
)
内部pkg_dir/__init__.py
:
import json
from os.path import dirname
with open(dirname(__file__) + '/pkg_info.json') as fp:
_info = json.load(fp)
__version__ = _info['version']
著者などの他の情報もpkg_info.json
に入れます。私
メタデータの管理を自動化できるため、JSONを使用します。
これらのソリューションの多くは、git
バージョンタグを無視しています。つまり、複数の場所でバージョンを追跡する必要があります(悪い)。私は次の目標でこれに取り組みました:
-
git tag
リポジトリ内のタグからすべてのPythonバージョン参照を取得します - 入力を必要としない単一のコマンドで
push
/setup.py upload
およびmake release
ステップを自動化します。
仕組み:
-
origin
コマンドから、gitリポジトリ内の最後のタグ付きバージョンが検出され、インクリメントされます。タグはMakefile
にプッシュバックされます。 -
src/_version.py
は、バージョンをsetup.py
に保存し、_version.py
によって読み取られ、リリースにも含まれます。 ソース管理にpackage.__version__
をチェックしないでください! -
release
コマンドは、next_minor_ver
から新しいバージョン文字列を読み取ります。
詳細:
メークファイル
# remove optional 'v' and trailing hash "v1.0-N-HASH" -> "v1.0-N"
git_describe_ver = $(shell git describe --tags | sed -E -e 's/^v//' -e 's/(.*)-.*/\1/')
git_tag_ver = $(shell git describe --abbrev=0)
next_patch_ver = $(shell python versionbump.py --patch $(call git_tag_ver))
next_minor_ver = $(shell python versionbump.py --minor $(call git_tag_ver))
next_major_ver = $(shell python versionbump.py --major $(call git_tag_ver))
.PHONY: ${MODULE}/_version.py
${MODULE}/_version.py:
echo '__version__ = "$(call git_describe_ver)"' > $@
.PHONY: release
release: test lint mypy
git tag -a $(call next_patch_ver)
$(MAKE) ${MODULE}/_version.py
python setup.py check sdist upload # (legacy "upload" method)
# twine upload dist/* (preferred method)
git push origin master --tags
next_major_ver
ターゲットは常に3番目のバージョンの数字をインクリメントしますが、versionbump.py
またはmy_module/_version.py
を使用して他の数字をインクリメントできます。コマンドは、リポジトリのルートにチェックインされるmy_module/__init__.py
スクリプトに依存しています
versionbump.py
"""An auto-increment tool for version strings."""
import sys
import unittest
import click
from click.testing import CliRunner # type: ignore
__version__ = '0.1'
MIN_DIGITS = 2
MAX_DIGITS = 3
@click.command()
@click.argument('version')
@click.option('--major', 'bump_idx', flag_value=0, help='Increment major number.')
@click.option('--minor', 'bump_idx', flag_value=1, help='Increment minor number.')
@click.option('--patch', 'bump_idx', flag_value=2, default=True, help='Increment patch number.')
def cli(version: str, bump_idx: int) -> None:
"""Bumps a MAJOR.MINOR.PATCH version string at the specified index location or 'patch' digit. An
optional 'v' prefix is allowed and will be included in the output if found."""
prefix = version[0] if version[0].isalpha() else ''
digits = version.lower().lstrip('v').split('.')
if len(digits) > MAX_DIGITS:
click.secho('ERROR: Too many digits', fg='red', err=True)
sys.exit(1)
digits = (digits + ['0'] * MAX_DIGITS)[:MAX_DIGITS] # Extend total digits to max.
digits[bump_idx] = str(int(digits[bump_idx]) + 1) # Increment the desired digit.
# Zero rightmost digits after bump position.
for i in range(bump_idx + 1, MAX_DIGITS):
digits[i] = '0'
digits = digits[:max(MIN_DIGITS, bump_idx + 1)] # Trim rightmost digits.
click.echo(prefix + '.'.join(digits), nl=False)
if __name__ == '__main__':
cli() # pylint: disable=no-value-for-parameter
これは、my_module
からバージョン番号を処理し、インクリメントする方法の面倒な作業を行います。
__ init __。py
<=>ファイルは<=>にインポートされます。モジュールと共に配布する静的インストール設定をここに配置します。
from ._version import __version__
__author__ = ''
__email__ = ''
setup.py
最後のステップは、<=>モジュールからバージョン情報を読み取ることです。
from setuptools import setup, find_packages
pkg_vars = {}
with open("{MODULE}/_version.py") as fp:
exec(fp.read(), pkg_vars)
setup(
version=pkg_vars['__version__'],
...
...
)
もちろん、これがすべて機能するためには、開始するためにリポジトリに少なくとも1つのバージョンタグが必要です。
git tag -a v0.0.1
Pythonパッケージにバージョン文字列を埋め込む標準的な方法はないようです。私が見たほとんどのパッケージは、あなたのソリューションのいくつかのバリアント、すなわちeitnerを使用しています
-
setup.py
にバージョンを埋め込み、version.py
に、パッケージによってインポートされたバージョン情報のみを含むモジュール(たとえば<=>)を生成させる、または -
逆:パッケージ自体にバージョン情報を入れ、 that をインポートして<=>
にバージョンを設定します
注目に値するのは、__version__
が準標準であることです。 Pythonでは__version_info__
もタプルです。単純な場合は次のようにできます。
__version__ = '1.2.3'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])
...また、ファイルから<=>文字列などを取得できます。
別のスタイルも見ました:
>>> django.VERSION
(1, 1, 0, 'final', 0)
矢印は興味深い方法で処理します。
arrow/__init__.py
内:
__version__ = '0.8.0'
VERSION = __version__
setup.py
内:
def grep(attrname):
pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
strval, = re.findall(pattern, file_text)
return strval
setup(
name='arrow',
version=grep('__version__'),
# [...]
)
NumPy distutilsを使用している場合の価値は、numpy.distutils.misc_util.Configuration
には make_svn_version_py()
変数package.__svn_version__
内のversion
内にリビジョン番号を埋め込むメソッド。
-
version.py
ファイルは、ファイル内の__version__ = <VERSION>
パラメーターでのみ使用します。setup.py
ファイルで__version__
パラメーターをインポートし、その値を次のようにversion=__version__
ファイルに入れます。version=<CURRENT_VERSION>
- 別の方法は、
version=<VERSION>
で.bumpversion.cfg
ファイルを使用することです-CURRENT_VERSIONはハードコードされています。
新しいタグ(新しいパッケージバージョンをリリースする準備ができている)を作成するたびにファイル内のバージョンを手動で変更したくないため、以下を使用できます。
bumpversion パッケージを強くお勧めします。私はこれを何年も使ってバージョンを上げてきました。
まだファイルがない場合は、[bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
ファイルにgit commit
を追加することから始めます。
バージョンをバンプするたびに、次のような短いスクリプトを使用する必要があります。
bumpversion (patch|minor|major) - choose only one option
git push
git push --tags
次に、呼び出されるリポジトリごとに1つのファイルを追加します:git reset
:
[bumpversion]
current_version = <CURRENT_TAG>
commit = True
tag = True
tag_name = {new_version}
[bumpversion:file:<RELATIVE_PATH_TO_SETUP_FILE>]
注:
- 他の投稿で提案されているように、<=>ファイルの下で<=>パラメーターを使用し、bumpversionファイルを次のように更新できます。 <=>
- レポジトリ内のすべてを必要 <=>または<=>しないと、ダーティレポジトリエラーが発生します。
- 仮想環境にbumpversionのパッケージが含まれていることを確認してください。これがないと動作しません。
CVS(またはRCS)を使用していて、迅速な解決策が必要な場合は、次を使用できます。
__version__ = "$Revision: 1.1 $"[11:-2]
__version_info__ = tuple([int(s) for s in __version__.split(".")])
(もちろん、リビジョン番号はCVSに置き換えられます。)
これにより、印刷しやすいバージョンと、インポートするモジュールに少なくとも予想されるバージョンがあることを確認するために使用できるバージョン情報が表示されます。
import my_module
assert my_module.__version_info__ >= (1, 1)