バージョンをPythonパッケージに埋め込む標準的な方法は?


265

バージョン文字列をpythonパッケージに関連付ける標準的な方法はありますか?

import foo
print foo.version

マイナー/メジャー文字列はsetup.pyすでに指定されているため、追加のハードコーディングなしでそのデータを取得する方法があると思います。私が見つけた別の解決策はimport __version__、私の中にfoo/__init__.pyあり、それによって__version__.py生成されたものsetup.pyです。


7
:FYI、で非常に良い概要がありますpackaging.python.org/en/latest/...は
ionelmc

1
インストールされたパッケージのバージョンは、setuptoolsを使用してメタデータから取得できるため、多くの場合、バージョンを入れるだけでsetup.py十分です。この質問を参照してください。
saaj 2015

2
参考までに、バージョン番号の(セットアップ時と実行時の両方で)単一の信頼できる情報源を維持するための基本的なパターン5つあります。
KFリン

@ionelmc Pythonのドキュメントには、シングルサワーの7つのオプションがリストされています。それは「単一の真の情報源」の概念と矛盾していませんか?
Stevoisiak

@StevenVascellaroあなたが何を求めているのかわからない。そこにリストされているパッケージのガイドは意見を表明したくないので、非常に多くの方法があります。
ionelmc

回答:


136

直接ではなく、あなたの質問への答えが、あなたはそれを命名検討すべき__version__ではなく、version

これはほぼ準標準です。標準ライブラリの多くのモジュールがを使用して__version__おり、これは多くのサードパーティモジュールでも使用されているため、準標準です。

通常__version__は文字列ですが、フロートまたはタプルの場合もあります。

編集:S.Lott(ありがとう!)が述べたように、PEP 8はそれを明示的に言っています:

モジュールレベルのDunder名

ようなモジュールレベル「dunders」(二つの先頭と2つの末尾下線付き即ち名)__all____author____version__等は、モジュールのドキュメンテーション文字列の後でから除く任意のインポートステートメントの前に配置されなければならない__future__輸入。

また、バージョン番号がPEP 440PEP 386はこの標準の以前のバージョン)で説明されている形式に準拠していることも確認してください。


9
これは文字列であり、タプルバージョンのversion_infoを持っている必要があります。
James Antill、

ジェームズ:なぜ__version_info__具体的に?(これはあなた自身の二重下線単語を「発明」します。)[Jamesがコメントしたとき、下線はコメントで何もしませんでしたが、強調を示しているので、Jamesも本当に書き__version_info__ました。--- ed。]

あなたは何について何か見ることができますバージョンで言うべきpackages.python.org/distribute/... ページがおよそ配布されていることをが、バージョン番号の意味は、デファクトスタンダードになりつつあります。
sienkiew 2010

2
正しい。これらのPEPは互いに矛盾しているようです。まあ、PEP 8は「if」と「crud」を言っているので、VCSキーワード拡張の使用を実際に推奨するものではありません。また、別のVCSに切り替えると、リビジョン情報が失われます。したがって、少なくとも大きなプロジェクトでは、単一のソースファイルに埋め込まれたPEP 386/440準拠のバージョン情報を使用することをお勧めします。
oefe 2014年

2
どこでその置くバージョンを。これが承認されたバージョンであることを考慮して、ここでその追加情報を参照してください。
darkgaze

120

私は、_version.pyバージョン情報を格納するための「1回の標準的な場所」として単一のファイルを使用します。

  1. __version__属性を提供します。

  2. 標準のメタデータバージョンを提供します。したがってpkg_resources、パッケージのメタデータを解析する他のツール(EGG-INFOまたはPKG-INFO、あるいはその両方、PEP 0345)によって検出されます。

  3. パッケージのビルド時にパッケージ(またはその他のもの)をインポートしないため、状況によっては問題が発生する可能性があります。(これが引き起こす可能性のある問題については、以下のコメントを参照してください。)

  4. バージョン番号が書き込まれる場所は1つしかないため、バージョン番号が変更されたときに変更できる場所は1つだけであり、バージョンの不整合が発生する可能性は低くなります。

これがどのように機能するかを示します。バージョン番号を格納する「1つの標準的な場所」は、「_ version.py」という名前の.pyファイルで、Pythonパッケージにあります(例:)myniftyapp/_version.py。このファイルは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はその文字列を "version"引数の値としてに渡し、setup()機能2を満たします。

機能1を満たすには、パッケージに(セットアップ時ではなく実行時に!)次のmyniftyapp/__init__.pyような_versionファイルをインポートします。

from _version import __version__

ここでこの技術の例 Iは長年使用してきたことは。

その例のコードはもう少し複雑ですが、このコメントに書いた簡単な例は完全な実装になるはずです。

ここでバージョンをインポートするサンプルコード

このアプローチに問題がある場合は、お知らせください。


8
#3の動機となる問題について教えてください。Glyphは、「setuptoolsがsetup.pyの実行時にシステムのどこにもないふりをするのを好む」と関係があると述べたが、詳細は私や他の人を説得するのに役立つだろう。
Ivan Kozik

2
@Ivaさて、ツールはこれをどの順序で実行するべきですか?(今日のsetuptools / pip / virtualenvシステムでは)を評価するまではdep 何であるかさえ知ることができませんsetup.py。また、これを行う前に完全な深さ優先ですべてのdepを実行しようとした場合、循環depがあるとスタックします。ただし、依存関係をインストールする前にこのパッケージをビルドしようとした場合setup.py、からパッケージをインポートしても、必ずしもそのdepや正しいバージョンのdepをインポートできるとは限りません。
Zooko、2011年

3
「setup.py」からファイル「version.py」を解析する代わりに書き込むことができますか?それはもっと簡単に思えます。
ジョナサンハートレー

3
Jonathan Hartley:「setup.py」が解析する代わりに「version.py」ファイルを書き込む方が少し簡単だと思いますが、setup.pyを編集すると、矛盾するウィンドウが開きます。新しいバージョンを持っているが、まだsetup.pyを実行してversion.pyファイルを更新していない。正規バージョンを小さな個別のファイルに含めるもう1つの理由は、リビジョン管理状態を読み取るツールなどの他のツールがバージョンファイルを書きやすくなることです。
Zooko

3
同様のアプローチは、execfile("myniftyapp/_version.py")手動でバージョンコードを解析するのではなく、setup.py内からです。stackoverflow.com/a/2073599/647002で提案-ディスカッションも役立つかもしれません。
medmunds 2013

97

2017-05を書き直し

Pythonコードの記述とさまざまなパッケージの管理を10年以上行った後、DIYはおそらく最善のアプローチではないという結論に達しました。

pbrパッケージのバージョン管理を扱うために、packageを使い始めました。SCMとしてgitを使用している場合、これは魔法のようなワークフローに適合し、数週間の作業を節約します(問題がどれほど複雑であるかに驚くでしょう)。

今日のところ、pbrはランク11で最も使用されているpythonパッケージであり、このレベルに到達するためのダーティトリックは含まれていませんでした。

pbr は、パッケージの保守作業をより多く行うことができ、バージョン管理に限定されませんが、そのすべての利点を採用することを強制するものではありません。

したがって、1つのコミットでpbrを採用する方法についてのアイデアを与えるには、pbrにパッケージングを入れ替えたような外観にします。

おそらく、バージョンがリポジトリにまったく保存されていないことに気付くでしょう。PBRはGitブランチとタグからそれを検出します。

アプリケーションをパッケージ化またはインストールするときにpbrがバージョンを「コンパイル」してキャッシュするため、gitリポジトリがない場合に何が起こるか心配する必要はありません。そのため、gitにランタイムの依存関係はありません。

古いソリューション

これが私がこれまでに見た中で最良の解決策であり、その理由も説明しています:

内部yourpackage/version.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'

内部yourpackage/__init__.py

from .version import __version__

内部setup.py

exec(open('yourpackage/version.py').read())
setup(
    ...
    version=__version__,
    ...

あなたがより良いと思われる別のアプローチを知っているなら私に知らせてください。


12
いえいえ。execfile()はPython 3には存在しないため、exec(open()。read())を使用することをお勧めします。
Christophe Vu-Brugier 2013年

4
なぜfrom .version import __version__setup.pyにもないのですか?
Aprillion '06 / 10/15

4
@Aprillion setup.py実行時にパッケージが読み込まれないため、試してみるとエラーが発生します(少なくとも、私はそうしました:
darthbith

3
pbrへのリンクにより、ゲートウェイが不良になります。
MERose 2017

4
間違いなくpbrは優れたツールですが、質問に答えることができませんでした。現在のバージョンまたはインストールされているパッケージにbpr経由でアクセスするにはどうすればよいですか。
nad2000

29

延期されたPEP 396(モジュールバージョン番号)に従って、これを行う方法が提案されています。それは、論理的根拠で、従うべきモジュールの(確かにオプションの)標準について説明しています。これがスニペットです:

3)モジュール(またはパッケージ)にバージョン番号が含まれている場合、バージョンは__version__属性で使用できる必要があります(SHOULD)。

4)名前空間パッケージ内に存在するモジュールの場合、モジュールには__version__属性を含める必要があります(SHOULD)。名前空間パッケージ自体には、独自の__version__属性を含めないでください。

5)__version__属性の値は文字列である必要があります。


13
そのPEPは承認/標準化されていませんが、延期されています(関心の欠如のため)。したがって、「標準的な方法がある」と明記されていると少し誤解を招きやすいのです。
ウィーバー

@ウィーバー:ああ、私の!新しいことを学びました。確認する必要があることを知りませんでした。
奇妙なことに、2014

4
これは標準ではないことに注意して編集されました。私はプロジェクトにこの「標準」に従うように要求する機能要求を上げたので、私は恥ずかしいと感じます。
奇妙なことに、

1
おそらくあなたは興味があるように見えるので、そのPEPの標準化作業を引き継ぐべきです:)
weaver

これは個々のモジュールのバージョン管理で機能しますが、完全なプロジェクトのバージョン管理に適用できるかどうかはわかりません。
Stevoisiak

21

これはおそらく遅すぎるかもしれませんが、前の回答の少し簡単な代替策があります:

__version_info__ = ('1', '2', '3')
__version__ = '.'.join(__version_info__)

(また、を使用して、バージョン番号の自動インクリメント部分を文字列に変換するのはかなり簡単str()です。)

もちろん、私が見てきたことから、人々はを使用するときに前述のバージョンのようなものを使用する傾向があり、そのためそれ__version_info__をintのタプルとして格納します。しかし、好奇心や自動インクリメント以外の目的でバージョン番号の一部に加算や減算などの数学的演算を実行する状況があるとは思えないので、そうする意味はあまりわかりません。int()そして、str())かなり容易に使用することができます。(一方、他の誰かのコードが文字列タプルではなく数値タプルを期待しているために失敗する可能性があります。)

これはもちろん私自身の見解であり、数値タプルを使用する上で他の人の入力を喜んで受け入れたいと思います。


sheziが私に思い出させたように、数値文字列の(字句)比較は、直接数値比較と同じ結果になるとは限りません。そのためには、先行ゼロが必要です。したがって、結局のところ、__version_info__整数値のタプルとして(または呼び出されるものは何でも)保存すると、より効率的なバージョン比較が可能になります。


12
いい(+1)ですが、文字列ではなく数字を使いませんか?例__version_info__ = (1,2,3)
orip 09年

3
文字列の比較は、バージョン番号が9を超えると危険になります。
D Coetzee、

13
I ..だけでなく、これを行うが、わずかにアドレスint型に微調整 __version_info__ = (0, 1, 0) __version__ = '.'.join(map(str, __version_info__))
rh0dium

2
問題__version__ = '.'.join(__version_info__)は、それ__version_info__ = ('1', '2', 'beta')がになるか1.2.beta、そうでない1.2beta1.2 beta
なぎさ

4
このアプローチの問題は、__ version__を宣言するコード行をどこに置くかだと思います。それらがsetup.pyにある場合、プログラムはパッケージ内からそれらをインポートできません。おそらくこれはあなたにとって問題ではありません。その場合は問題ありません。それらがプログラム内にある場合、setup.pyはそれらをインポートできますが、プログラムの依存関係がまだインストールされていない場合、インストール中にsetup.pyが実行されるため、そうする必要はありません(setup.pyは依存関係を判別するために使用されます) 。)したがってZookoの答え:製品パッケージをインポートせずに、手動で製品ソースファイルから値を解析します
Jonathan Hartley

11

ここでこれらのソリューションの多くはgitバージョンタグを無視します。つまり、複数の場所でバージョンを追跡する必要があります(悪い)。私は次の目標でこれに取り組みました:

  • gitリポジトリ内のタグからすべてのPythonバージョン参照を取得する
  • 入力を必要としない単一のコマンドで自動化およびgit tag/ ステップします。pushsetup.py upload

使い方:

  1. make releaseコマンドから、gitリポジトリ内の最後にタグ付けされたバージョンが見つかり、インクリメントされます。タグはに戻されoriginます。

  2. Makefile店のバージョンsrc/_version.py、それはによって読み取られる場所setup.pyともリリースに含まれます。ソース管理にチェックインしないでください_version.py

  3. setup.pyコマンドは、から新しいバージョンの文字列を読み取りますpackage.__version__

詳細:

Makefile

# 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

releaseターゲットは、常に第三版の数字をインクリメントしていますが、使用することができますnext_minor_verまたはnext_major_ver他の数字をインクリメントします。コマンドversionbump.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

これは、バージョン番号を処理してバージョン番号を1からインクリメントするための非常に面倒な方法を実行しますgit

__init__.py

my_module/_version.pyファイルをにインポートされますmy_module/__init__.py。モジュールとともに配布する静的インストール構成をここに配置します。

from ._version import __version__
__author__ = ''
__email__ = ''

setup.py

最後のステップは、my_moduleモジュールからバージョン情報を読み取ることです。

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

1
実際、このスレッド全体では、VCSがこの議論で非常に重要であることを忘れています。単なるobs:バージョンのインクリメントは手動プロセスのままにする必要があるため、推奨される方法は1.手動でタグを作成してプッシュする2. VCSツールにそのタグを検出させ、必要な場所に保存させる(すごい-このSO編集インターフェースは本当に私を困らせている) -改行を処理するためだけにこれを数十回編集する必要があり、
それでも機能し

Python用のversionbump.py素晴らしいバンプバージョンパッケージがある場合は使用する必要はありません。
オラン

@Oran versionbumpを見ました。ドキュメントはあまり明確ではなく、タグ付けをうまく処理しません。私のテストでは、クラッシュする状態になるようです:subprocess.CalledProcessError:Command '[' git '、' commit '、' -F '、' / var / folders / rl / tjyk4hns7kndnx035p26wg692g_7t8 / T / tmppishngbo '] 'はゼロ以外の終了ステータス1を
返しました

1
_version.pyバージョン管理で追跡すべきでないのはなぜですか?
Stevoisiak

1
@StevenVascellaroこれはリリースプロセスを複雑にします。次に、タグとコミットが_version.pyの値と一致していることを確認する必要があります。単一のバージョンタグを使用すると、IMOがより簡潔になり、github UIから直接リリースを作成できることを意味します。
cmcginty

10

パッケージ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を使用するのが好きです。


jsonを使用してメタデータ管理を自動化する方法を詳しく説明していただけませんか?ありがとう!
ryanjdillon

6

また__version__、セミスタンダードであることに加えて、注目に値します。Pythonではso is __version_info__is a tupleですが、単純なケースでは次のようにすることができます:

__version__ = '1.2.3'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])

...そして__version__、ファイルなどから文字列を取得できます。


1
この使用法の起源に関する参照/リンクはあり__version_info__ますか?
クレイグマックイーン

3
これはsys.versionからsys.version_infoへのマッピングと同じです。したがって:docs.python.org/library/sys.html#sys.version_info
James

7
他の方向(__version_info__ = (1, 2, 3)および__version__ = '.'.join(map(str, __version_info__)))でマッピングを行う方が簡単です。
Eric O Lebigot 2013

2
@EOL-- __version__ = '.'.join(str(i) for i in __version_info__)少し長くなりますが、Pythonicです。
ArtOfWarfare 2016年

2
あなたが提案するものは、本当に必要ではなく、意味を表現するのが少し難しいダミー変数を含んでいるので、あなたが提案するものは明らかにもっとpythonicであるかわかりません(i意味がなくversion_num、少し長く曖昧です...)。map()ここで行う必要があるのはmap()(既存の関数での使用)の一般的なユースケースであるため、Pythonでの存在をここで使用する必要があるという強いヒントとしても取り上げます。他の多くの合理的な使用法はありません。
Eric O Lebigot 2016年

5

Pythonパッケージにバージョン文字列を埋め込む標準的な方法はないようです。私が見たほとんどのパッケージは、あなたのソリューションのいくつかのバリアント、すなわちeitnerを使用しています

  1. バージョンを埋め込み、バージョン情報のみを含むモジュール(例:)setup.pysetup.py生成version.pyします。これは、パッケージによってインポートされます。または

  2. 逆:パッケージ自体にバージョン情報を入れ、それをインポートてバージョンを設定します setup.py


私はあなたの推奨が好きですが、setup.pyからこのモジュールを生成する方法は?
ソリン

1
私はオプション(1)のアイデアが好きです。ファイルからバージョン番号を解析するというZookoの回答よりも簡単に思えます。ただし、開発者がリポジトリを複製するだけでversion.pyが作成されることを保証することはできません。version.pyでチェックインしない限り、setup.pyでバージョン番号を変更し、コミットしてリリースし、変更をversion.pyにコミットする必要があります(スラッシュを忘れます)。
ジョナサンハートレー

おそらく、 "" "with open(" mypackage / version.py "、" w ")のようなものをfpとして使用してモジュールを生成できます:fp.write(" __ version__ == '%s' \ n "%(__version__、) ) "" "
ジョナサンハートレー

1
JABの回答へのコメントに記載されているように、オプション2.はインストール中に失敗する可能性があると思います。
ジョナサンハートレー

そのことについて何?ソフトウェアのメインパッケージの__init__.py "に__version__ = '0.0.1'"(バージョンはもちろん文字列です)を入れます。次に、ポイント2に進みます。セットアップで、package .__ init__ import __version__をvとして実行し、変数vをsetup.pyのバージョンとして設定します。次にmypackをmyとしてインポートし、print my .__ version__はバージョンを印刷します。バージョンは、ソフトウェアに関連する他のものをインポートしないファイル内の、コード全体からアクセス可能な1か所のみに保存されます。
SeF 2017

5

矢印はそれを興味深い方法で処理します。

現在(2e5031b以降)

arrow/__init__.py

__version__ = 'x.y.z'

setup.py

from arrow import __version__

setup(
    name='arrow',
    version=__version__,
    # [...]
)

arrow/__init__.py

__version__ = 'x.y.z'
VERSION = __version__

setup.py

def grep(attrname):
    pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
    strval, = re.findall(pattern, file_text)
    return strval

file_text = read(fpath('arrow/__init__.py'))

setup(
    name='arrow',
    version=grep('__version__'),
    # [...]
)

なにfile_text
e

2
更新されたソリューションは実際には有害です。setup.pyが実行されている場合、ローカルファイルパスからパッケージのバージョンが表示されるとは限りません。pip install -e .テスト時に、開発ブランチでの実行などから、以前にインストールされたバージョンに戻る場合があります。setup.pyは、デプロイメントのパラメーターを決定するために、インストールプロセス中のパッケージのインポートに絶対に依存すべきではありません。うわぁ。
エリー

はい、なぜarrowがこのソリューションに後退することを決めたのか分かりません。また「で更新setup.pyメッセージが言うコミット近代的なPythonの標準 🤷...」
ANTO

4

私はまた別のスタイルを見ました:

>>> django.VERSION
(1, 1, 0, 'final', 0)

1
はい、私も見ました。ところですべての答えは他のスタイルを取るので、私は今、どのスタイルが「標準」であるのかわかりません。言及されたPEPを探しています...
kbec

別の見方; MongoのPythonクライアントは、アンダースコアなしのプレーンバージョンを使用します。したがって、これは機能します。$ python >>> pymongoをインポート>>> pymongo.version '2.7'
AnneTheAgile

実装.VERSIONしても、実装する必要はありません__version__
Acumenus 2017

これdjangoはプロジェクトに実装する必要がありますか?
Stevoisiak

3

使用するsetuptoolsと、pbr

バージョンを管理する標準的な方法はありませんが、パッケージを管理する標準的な方法はsetuptoolsです。

バージョンを管理するために私が全体的に見つけた最良の解決策はsetuptoolspbr拡張機能を使用することです。これがバージョン管理の私の標準的な方法になりました。

完全なパッケージング用にプロジェクトを設定することは、単純なプロジェクトではやり過ぎかもしれませんが、バージョンを管理する必要がある場合は、おそらくすべてを設定するだけの適切なレベルにあります。これにより、パッケージがPyPiリリース可能になり、誰もがダウンロードしてPipで使用できるようになります。

PBRは、ほとんどのメタデータをsetup.pyツールからsetup.cfgファイルに移動します。このファイルは、バージョンを含むことができるほとんどのメタデータのソースとして使用されます。これにより、pyinstaller必要に応じてメタデータを実行可能ファイルにパッケージ化できるようになり(必要な場合は、おそらくこの情報が必要になります)、メタデータを他のパッケージ管理/セットアップスクリプトから分離します。バージョン文字列は直接setup.cfg手動で更新でき*.egg-info、パッケージリリースのビルド時にフォルダーにプルされます。スクリプトは、さまざまな方法を使用してメタデータからバージョンにアクセスできます(これらのプロセスについては、以下のセクションで説明します)。

VCS / SCMにGitを使用する場合、Gitから多くのメタデータを取り込むため、このセットアップはさらに優れています。バージョン、作成者、変更ログなど、一部のメタデータの主要な真の情報源としてリポジトリを使用できます。などバージョンについては、リポジトリのgitタグに基づいて現在のコミットのバージョン文字列を作成します。

PBRはバージョン、作成者、変更ログ、その他の情報をgitリポジトリから直接プルsetup.cfgするため、パッケージのディストリビューションが作成されるたびに(を使用してsetup.py)メタデータの一部を省略して自動生成できます

リアルタイムの現在のバージョン

setuptoolsを使用してリアルタイムで最新情報を取得しsetup.pyます:

python setup.py --version

これによりsetup.cfg、作成された最新のコミットとリポジトリに存在するタグに基づいて、ファイルまたはgitリポジトリから最新バージョンがプルされます。ただし、このコマンドはディストリビューションのバージョンを更新しません。

バージョンの更新

を使用してディストリビューションを作成するとsetup.pypy setup.py sdistたとえば、)、現在のすべての情報が抽出され、ディストリビューションに保存されます。これは基本的にsetup.py --versionコマンドを実行し、そのバージョン情報を、package.egg-info配布メタデータを格納する一連のファイル内のフォルダーに格納します。

バージョンメタデータを更新するプロセスに関する注意:

pbrを使用してgitからバージョンデータをプルしていない場合は、setup.cfgを新しいバージョン情報で直接更新します(簡単ですが、これがリリースプロセスの標準部分であることを確認してください)。

gitを使用していて、(python setup.py sdistまたはいずれかのpython setup.py bdist_xxxコマンドを使用して)ソースまたはバイナリの配布を作成する必要がない場合、git repo情報を<mypackage>.egg-infoメタデータフォルダーに更新する最も簡単な方法は、python setup.py installコマンドを実行することです。これにより、gitリポジトリからメタデータをプルしてローカル.egg-infoフォルダーを更新することに関連するすべてのPBR関数が実行され、定義したエントリポイントのスクリプト実行可能ファイルがインストールされます。また、このコマンドを実行すると、出力から確認できるその他の関数がインストールされます。

ことを注意.egg-infoフォルダは、一般的にはgitに格納されてからは除外されるが、標準のPython自体をレポ.gitignore(などからなどのファイルGitignore.IOそれはあなたのソースから生成することができるように、)。除外されている場合は、リリース前にローカルでメタデータを更新する標準の「リリースプロセス」があることを確認してください。PyPi.orgにアップロードまたは配布するパッケージには、このデータを含めて正しいバージョンにする必要があります。あなたがしたい場合はGitは(すなわち追加し、あなたが無視されてから特定のファイルを除外することができ、この情報を含むようにレポ!*.egg-info/PKG_INFO.gitignore

スクリプトからバージョンにアクセスする

パッケージ自体のPythonスクリプト内で、現在のビルドのメタデータにアクセスできます。たとえば、バージョンの場合、これを行うにはいくつかの方法があります。

## This one is a new built-in as of Python 3.8.0 should become the standard
from importlib-metadata import version

v0 = version("mypackage")
print('v0 {}'.format(v0))

## I don't like this one because the version method is hidden
import pkg_resources  # part of setuptools

v1 = pkg_resources.require("mypackage")[0].version
print('v1 {}'.format(v1))

# Probably best for pre v3.8.0 - the output without .version is just a longer string with
# both the package name, a space, and the version string
import pkg_resources  # part of setuptools

v2 = pkg_resources.get_distribution('mypackage').version
print('v2 {}'.format(v2))

## This one seems to be slower, and with pyinstaller makes the exe a lot bigger
from pbr.version import VersionInfo

v3 = VersionInfo('mypackage').release_string()
print('v3 {}'.format(v3))

__init__.py他のいくつかの回答と同様に、次のようにパッケージのバージョン情報を抽出するために、これらの1つを直接に入れることができます。

__all__ = (
    '__version__',
    'my_package_name'
)

import pkg_resources  # part of setuptools

__version__ = pkg_resources.get_distribution("mypackage").version

質問に直接答えるために、反対票ごとに再フォーマットしました。
LightCC

1

最も単純で信頼性の高い解決策を見つけるために数時間試行した後、次の部分を示します。

パッケージ「/ mypackage」のフォルダー内にversion.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__ = '1.2.7'

setup.py:

exec(open('mypackage/version.py').read())
setup(
    name='mypackage',
    version=__version__,

メインフォルダーinit .py:

from .version import __version__

exec()モジュールをインポートすることができます前に、setup.pyが実行されているので機能は、任意の輸入のスクリプト外を実行します。バージョン番号は1つのファイルで1か所で管理するだけで十分ですが、残念ながらsetup.pyにはありません。(それは欠点ですが、インポートのバグがないことが利点です)


1

この質問が最初に尋ねられて以来、統一されたバージョン管理に向けて、そして規約をサポートするための多くの作業が完了しました。嗜好性のあるオプションの詳細については、Pythonパッケージユーザーガイドをご覧ください。また、注目に値するのは、バージョン番号のスキームがPEP 440のPythonで比較的厳格であるため、パッケージがCheese Shopにリリースされる場合、正常な状態を維持することが重要です。

バージョン管理オプションの短い内訳は次のとおりです。

  1. setup.pysetuptools)でファイルを読み取り、バージョンを取得します。
  2. 外部ビルドツール(__init__.pyソース管理だけでなくソース管理も更新する)を使用します(例:bump2versionchangeszest.releaser)
  3. __version__特定のモジュールのグローバル変数に値を設定します。
  4. setup.pyとコードの両方を読み取るために、値を単純なVERSIONテキストファイルに配置します。
  5. setup.pyリリース経由で値を設定し、importlib.metadataを使用して実行時に取得します。(警告、3.8より前のバージョンと3.8より後のバージョンがあります。)
  6. 値を__version__inに設定し、sample/__init__.pyサンプルをにインポートしsetup.pyます。
  7. setuptools_scmを使用してソース管理からバージョン管理を抽出し、コードではなく正規参照になるようにします。

(7)が最も最新のアプローチである可能性があることに注意してください(ビルドメタデータは、オートメーションによって公開されたコードから独立しています)。また、パッケージのリリースにセットアップが使用されている場合は、シンプルなバージョンで直接レポートされることに注意してくださいpython3 setup.py --version


-1

あなたがnumpyののdistutilsのを使用している場合はどのようなことの価値については、numpy.distutils.misc_util.Configuration持っているmake_svn_version_py()リビジョン番号の内部を埋め込む方法package.__svn_version__変数にはversion


これがどのように機能するかの詳細または例を提供できますか?
Stevoisiak

うーん。2020年には、これは(常にそうでしたか?)FORTRANのためのものです。パッケージ "numpy.distutilsは、Fortranソースを処理するためにNumPy拡張標準Python distutilsの一部です。"
ingyhere

-1
  1. version.pyファイルに__version__ = <VERSION>param を含むファイルのみを使用してください。でsetup.py、ファイルのインポート__version__のparamをし、それの値をつけsetup.py、このようなファイル: version=__version__
  2. 別の方法は、setup.pyファイルのみを使用することですversion=<CURRENT_VERSION>-CURRENT_VERSIONはハードコードされています。

新しいタグを作成するたびに(新しいパッケージバージョンをリリースする準備ができている)ファイルのバージョンを手動で変更したくないので、以下を使用できます。

バンプバージョンパッケージを強くお勧めします。私は何年もバージョンをぶつけるためにそれを使ってきました。

まだファイルがない場合はversion=<VERSION>setup.pyファイルに追加することから始めます。

バージョンをバンプするたびに、次のような短いスクリプトを使用する必要があります。

bumpversion (patch|minor|major) - choose only one option
git push
git push --tags

次に、呼び出されたリポジトリごとに1つのファイルを追加します.bumpversion.cfg

[bumpversion]
current_version = <CURRENT_TAG>
commit = True
tag = True
tag_name = {new_version}
[bumpversion:file:<RELATIVE_PATH_TO_SETUP_FILE>]

注意:

  • 使用できます __version__version.py他の投稿で提案されているように、ファイルの下のパラメーター、次のようにバンプバージョンファイルを更新できます。 [bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
  • する必要あります git commitまたはgit resetあなたのリポジトリ内のすべてのもので、そうでなければ、あなたはダーティレポエラーを受け取ります。
  • 仮想環境にbumpversionのパッケージが含まれていることを確認してください。含まれていないと機能しません。

@cmcginty遅延のため申し訳ありませんが、私の回答を確認してください^^^-リポジトリのすべてをgit commitまたはgit resetして、仮想環境にのパッケージが含まれていることを確認してくださいbumpversion。最新バージョンを使用してください。
オラン

ここで提案されている解決策については少しわかりません。でバージョンを手動で追跡することをお勧めversion.pybumpversionますか、それともで追跡しますか?
Stevoisiak

@StevenVascellaroバンプバージョンを使用することをお勧めします。手動バージョン管理は使用しないでください。私が説明しようとしたのは、bumpversionを指示して、setup.pyファイルのバージョンを更新するか、それを使用してversion.pyファイルを更新できることです。version.pyファイルを更新し、__version__param値をsetup.pyファイルに取り込むのがより一般的な方法です。私のソリューションは本番環境で使用されており、一般的な方法です。注:明確にするために、スクリプトの一部としてバンプバージョンを使用するのが最良の解決策です。CIに配置すると、自動操作になります。
オラン

-3

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)

保存__version__を推奨するファイルは何ですか?このソリューションでバージョン番号をインクリメントするにはどうすればよいですか?
Stevoisiak
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.