3 versioning policy
Versioning policy
Def: Versioning
- Versioningとは,パッケージの異なるバージョンにユニークな識別子を追加するプロセスのこと
- 多くのPython packageではsemantic versioningが用いられる
ソースコードを変更すると,昔使えたmethodが使えなくなったり,他のパッケージとの互換性問題から動作しなくなったりする可能性があります. パッケージユーザーがプログラム動作や分析の再現性保証ができるように,開発者はパッケージの各ユニークな状態にユニークなバージョン番号を割り当て, それぞれの新しいバージョンを独立してリリースすることが求められます.
▶ versioningのメリット
- パッケージユーザーが特定のversionのパッケージを指定してインストールできるようになる
- versioning infoを通して,バグ修正や新機能追加などパッケージの変更内容をユーザーに伝えることができる
- 依存関係管理を容易にする
mypackage
という新しいパッケージを開発したとします.このパッケージは,Agito
というパッケージを利用しており, また Agito 3.0.0
で追加された機能を利用しているとします.このとき,mypackage
を利用するプロジェクトでは, < 3.0.0
のAgito
が利用できないことになります.
適切なversioning policyを用いてれば,pyproject.toml
などの開発環境設定ファイルに
dependencies = [
"Agito>=3.0.0,<4.0.0",
]
と記載することで依存関係管理することができるようになります.
Version numbering
📘 Summary
- semantic versioningに従ったversion numberは
MAJOR.MINOR.PATCH
の3つのint > 0から構成される MAJOR
: 後方互換性のない変更を入れた場合MINOR
: 後方互換性のあるenhancementを入れた場合PATCH
: 後方互換性のあるBUG FIXを入れた場合- versioningされたパッケージがリリースされたされた場合,そのバージョンの内容を変更してはならない.変更を加える場合は,必ず新しいバージョンとしてリリースすること
1.0.0
はパッケージの最初の安定版リリースに使用される
semantic versioningのVersion numberingをここでは取り扱います. 一般的には,ソフトウェアの最初のバージョンは通常 0.1.0
から始まり,開発進捗/リリースに合わせてMAJOR.MINOR.PATCH
のインクリメントが行われます. version numberを増加 = インクリメントさせることを,version bumpingと呼びます.
▶ Patch release (0.1.0 → 0.1.1)
Patch releaseは後方互換性のあるBUG FIXを入れた場合に行われます.後方互換性とは,パッケージバージョンをアップグレードしても, 以前に記述したコードがそのまま動作することを意味します.
ユーザー目線で操作性に影響を与えないような,内部的なバグ変更がPatch releaseの対象となります.
▶ Minor release (0.1.0 -> 0.2.0)
Minor releaseは,大規模なバグ修正や後方互換性を保ちながら追加される新機能(例: 新しいmethodの追加)を加えた場合に行われます. Minor releaseに伴うversion bumpingがなされる際,PATCH
は 0
に戻る必要があります.
▶ Major release (0.1.0 -> 1.0.0)
1.0.0
はパッケージの最初の安定版リリースに使用されます.その後,後方互換性のない変更や多くのユーザーに影響を与える変更が行われた場合に, Major releaseが行われます.後方互換性のない変更は breaking changes と呼ばれます.
パッケージ内のモジュール名を変更することはbreaking changesの一例です.この場合,ユーザーが新しいパッケージにアップグレードすると, 古いモジュール名を使用していたコードが動作しなくなり,コードを修正する必要が生じます.
Python packageでの実装
Python Packaging User Guide > Single-sourcing the Project Version にて
Many Python distribution packages publish a single Python import package where it is desired that the runtime version attribute on the import package report the same version specifier as
importlib.metadata.version()
reports for the distribution package
つまり,
- package versionは
__version__
attributeで参照できるようにするのが望ましい __version__
attributeの内容はimportlib.metadata.version()
と一致するのが望ましい(つまり,import_name.__version__
,importlib.metadata.version("dist-name")
が一致する)
とされています.しかし,git/GitHubを用いた開発ではversionをgit tagという形で格納していたりしますし,Poetryなどのpackage-management toolを用いている場合は pyproject.toml
に記載されていたりします.これらをより考えるべき問題は,version情報を格納したdata entryをどこにすべきなのか,ということになります.
非推奨: __init__.py
でのハードコーディング
transformer
, QuantEconpy
, sckit-learn
などでは,ソースコード格納ディレクトリ直下の __init__.py
に
__init__.py
= "1.7.dev0" __version__
といった形式でversion情報が記載されています.この方法は,現在では非推奨とされています.
▶ 非推奨の理由
- パッケージのバージョンを更新するたびに,
__init__.py
を手動で変更する必要があり,変更し忘れが発生しやすい - バージョン情報を
__init__.py
に直接記述していると,ビルドツールやCI/CDパイプラインでのバージョン管理や更新が難しくなる
推奨: pyproject.toml
による管理
PEP 621で紹介されているように,現在ではpyproject.toml
を使ったメタデータ管理が推奨されます. ポイントは,
- version情報はprojectメタ情報を記載する
pyproject.toml
で取り扱う __version__
attributeはpyproject.toml
に記載されたversion情報を参照するようにする
この場合,pyproject.toml
, __init__.py
にそれぞれのラインを記載します
pyproject.toml
[project]
name = "package_name"
version = "1.0.0"
python runtime中でも__version__
attribute経由で確認できるように
__init__.py__
from importlib.metadata import version
= version("package_name") __version__
も設定します.importlib.metadata
はパッケージメタ情報についてのインターフェースを提供するモジュールです(Python 3.8以降に導入). package versionを取得したい場合は,versionを取得したいパッケージ名をimportlib.metadata.version()
の引数に設定すると, METADATA
ファイルのversion fieldよりversion infoを取得してくれます.
METADATA
ファイルは,build toolによって異なりますが,一般的には<package_name>.<version>.dist-info
に格納されています. <package_name>.<version>.dist-info
ディレクトリ内の METADATA
ファイルは, パッケージがビルドされてインストールされる際に pyproject.toml
(およびその他のビルド構成ファイル)から生成されるという関係性があります.
なので,importlib.metadata.version()
は pyproject.toml
の内容を参照していると理解して良いと思います.
Example 3.1 : importlib.metadata
によりversion info取得
from importlib.metadata import version
# Get the version of a package (e.g., 'requests')
= version('requests')
package_version print(f"The version of the 'requests' package is: {package_version}")
OutPut
The version of the 'requests' package is: 2.28.1
Class.show_version()
: 継承を用いてClassからversion情報を表示する
個人のプラクティスとなりますが,パッケージ開発にてClass開発をする場合,BaseMetaInfo
クラスを定義 & 継承することで メインのClassからshow_version()
methodを叩くことでversion infoを実行環境と合わせて確認できるようにしています.
import importlib.metadata
import os
import platform
import pprint
import sys
class BaseMetaInfo:
@classmethod
def show_version(cls):
= "synth_coremodel"
cls.class_name = platform.uname()
uname_result = OrderedDict(
version_info
{
cls.class_name: importlib.metadata.version(cls.class_name),"python-version": ".".join([str(i) for i in sys.version_info]),
"OS": uname_result.system,
"OS-release": uname_result.release,
"processor": uname_result.processor,
"number of available cpu": os.cpu_count(),
}
) pprint.pprint(version_info)
例として,regmonkey_analysis
パッケージでLinearEstimator
というClassを実装した場合,
from regmonkey_analysis import LinearEstimator
LinearEstimator.show_version()
OutPut
OrderedDict([('regmonkey_analysis', '6.1.2'),
('python-version', '3.11.8.final.0'),
('OS', 'Linux'),
('OS-release', '6.8.0-49-generic'),
('processor', 'x86_64'),
('number of available cpu', 32)])
と表示できるようにしています