バージョン番号を2度書かないために

Python では、下らない問題は、賢く綺麗に解決しようとせずに、 Python 本体が持っている柔軟さを使って強引に解決する方が楽な場合があります。
問題ごとに綺麗な仕組みを作ってしまうと、新しく Python を覚えるひとは無数にある下らない問題の数だけ用意された仕組みを覚えないといけなくなってしまうからです。

今日は下らない問題の例として、配布するモジュールにバージョン番号をもたせる場合について考えてみます。

# mymodule/__init__.py
__version__ = '0.0.1'

さて、 setup スクリプトを書きます。

# setup.py
from distutils.core import setup

setup(name="mymodule",
      version='0.0.1',
      packages=["mymodule"],
      )

バージョン番号を2ヶ所に書いてしまいました。これは危険です。リリース手順書書いて、両方更新してからバージョン管理システムのタグをつけるんだって明文化して、リリースするたびにリリース手順書を確認しないといけません。僕は物ぐさなんでそんなの嫌です。なので、こうしましょう。

# setup.py
from distutils.core import setup
from mymodule import __version__

setup(name="mymodule",
      version=__version__,
      packages=["mymodule"],
      )

これでだいたいオッケーですが、これでも問題が出るケースがあります。例えば、 mymodule がなにか拡張モジュールを持っていて、 import mymodule しただけでその拡張モジュールが読み込まれる場合には、 setup.py build_ext -i とかやって拡張モジュールをビルドしないと import mymodule が失敗するかもしれません。他にも、例えばソースコードを 2to3 を使って変換する場合なんかでも、setup.pyが実行されるときは 2to3 による変換前なので、 import mymodule が Python 3 で実行できないかもしれません。というか、そういう場面に直面しました。なので次のように強引かつシンプルに解決しました。

# mymodule/_version.py
__version__ = '0.0.1'

# mymodule/__init__.py
from ._version import __version__

# setup.py
from distutils.core import setup
exec(open('mymodule/_version.py').read())

setup(name="mymodule",
      version=__version__,
      packages=["mymodule"],
      )

特にオススメはしませんが、 Python 3 対応で同じ問題にハマった人がいたら参考にしてください。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。