Python のライブラリでは、よく __version__
が定義されていて、
インタラクティブシェルで、簡便なバージョン確認に使えますが、
>>> import pandas
>>> pandas.__version__
'0.21.0'
自分でライブラリを書くのに、__verson__
を定義するのはアンチパターンじゃないかと、最近思い始めました。
じゃあどうするの?
こうします。
-
__version__
は定義しない - バージョンは
setup.py
に(だけ)指定する
以下理由です。
理由1: 二重管理になる
バージョン番号は、setup.pyにも書かなくていけません。
# mylibrary.py
__version__ = '1.2.3'
# setup.py
from setuptools import setup
import mylibrary
setup(
version='1.2.3',
...
)
ファイルを2つ編集するのは面倒ですね。
面倒だけならまだいいのですが、両者のバージョンがズレてしまったらどうするのか?バージョンを合わせるだけのために、新しいバージョンをリリースするのか?
理由2: setup.py が本体のコードに依存してしまう
二重管理を避けるために、setup.py で__version__
を参照する方法もよく取られています。
# setup.py
from setuptools import setup
import mylibrary
setup(
version=mylibrary.__version__,
...
)
しかしこれは、mylibrary
がどこでも容易にimportできることを暗黙に仮定しています。もし、
-
mylibrary
が内部で他のライブラリに依存していたら? -
mylibrary
がC言語拡張モジュールだったら? -
mylibrary
が import にすごく時間がかかる巨大なライブラリだったら?
特に1番目のケースでは、依存ライブラリのインストールに setup.py を実行しなけりゃならないが、mylibrary
が import できないので setup.py が実行できない!
って状況になります。
理由3: 無くても困らない
そんなにバージョンを知りたいですか?
別に、バージョンを知りたければ、pip freeze
コマンドでよくないですか?
$ pip freeze | grep mylibrary
mylibrary==1.3.4
依存ライブラリのバージョンごとに振る舞いを変えるために、こんなポリフィルを書くことがありますが、
if mylibrary.__version__ >= '1.4.0':
something = mylibrary.something
else:
def something():
mylibrary.something のポリフィル
しかし、そもそもこれは適切なのか?
バージョン関係の処理は ライブラリの責務なのでしょうか?。
あんまり頑張るのではなく、単に古い依存ライブラリは切ってしまう。それが、古いライブラリの更新を促し、結果的にみんなが幸せになるのではないかとも思います。
なお、どうしてもプログラム的にバージョンを取りたいなら、pkg_resources
を使うこともできます。
import pkg_resources
v = pkg_resources.get_distribution('mylibrary').version
理由4: そもそも標準でもなんでもない
__version__
はなんとなくみんな定義しているだけで、定義しなくてもよいものです。「__version__
を追加してください」ってIssueは建てられるかもしれませんが。
標準では無いので、バージョンを確実に取得したいなら、上述のpkg_resources
を使わざるをえません。そして、pkg_resources
では __version__
は不要です。
感想
SQLアンチパターンみたいな本を読むと、アンチパターンって、「簡単にできる」とか「一見シンプル」なものが多いような気がするんですが、__version__
はまさにそれだなって思います。初心者でも簡単にできるし、一見シンプルで賢そうな解決。でも後々困る。