2013.12 版 python2.4-3.3 までのテストツール対応状況

ここしばらく python 2.5 に対応した python コードを書いていなかったので気にもとめませんでしたが、
diff-highlight を書くときに python 2.4, 2.5 対応でハマったことについてまとめます。
ちなみに diff-highlight を python 2.4, 2.5 に対応させたのは、
連携先である mercurialpython 2.4-2.7 をサポートしているので、それに合わせたためです。

ちなみに、タイトルに python2.4-3.3 とありますが、この記事では 3.0 と 3.1 は対象としていません。

python 2.4, 2.5 パッケージを手に入れる (Ubuntu)

私がテスト実行環境として使っている drone.io では ubuntu (おそらく 10.04)を使っていますが、
ubuntu の python2.4 および python2.5 パッケージは提供されていません。

そこで、deadsnakes リポジトリ で提供されている deb パッケージを利用します。

add-apt-repository を使って deadsnakes リポジトリを追加するだけで
すぐに python 2.4, 2.5 パッケージが利用できるようになります。

$ sudo add-apt-repository ppa:fkrull/deadsnakes
$ sudo apt-get update
$ sudo apt-get install python2.4 python2.4-dev python2.5 python2.5-dev python2.6 python2.6-dev

python 2.4, 2.5 のテスト実行は特定の環境を作る必要がある


という叫びの通り、setuptools 2.0 の登場によって python 2.4 と 2.5 は単純な方法ではテストしづらくなりました。
詳しくは 世界中のtoxテストがsetuptools-2.0リリース日(12月7日)以降Python-2.5のテストで失敗してる話 で解説されていますが、
特定のバージョンを指定して環境を作る必要があります。

先ほどの記事では python 2.5 向けの環境として pip 1.3、virtualenv 1.9.1、tox 1.5 の組み合わせを紹介していますが、
virtualenv は 1.8 で python 2.4 のサポートを終了しているので
python 2.4, 2.5 の両方に使える環境として distribute、pip 1.3、virtualenv 1.7.2、tox 1.4 を組み合わせます。
訂正 12/24: distribute をインストールしなくても良いことがわかったので削除しました。

また、先ほどの記事では python 2.5 に対して直接 virtualenv, tox を入れていますが、
virtualenv を使うことで次のように python 2.4, 2.5 向け環境を作ることができます。

$ virtualenv old_pythons
$ old_pythons/bin/pip install pip==1.3 virtualenv==1.7.2 tox==1.4

インストール済みの virtualenv を使って新たに環境を作り、
その中に distribute、pip 1.3、virtualenv 1.7.2、tox 1.4 をインストールします(pip と virtualenv はダウングレード)。

あとは old_pythons/ にインストールした tox を使って python 2.4, 2.5 のテストを実行します

$ old_pythons/bin/tox -e py24,py25

tox の -e オプションは tox.ini の envlist の代わりにテスト対象を指定するので、
tox.ini に py24, py25 の宣言がなくてもテストを行うことができます。

diff-highlight のテストでは

  • py24, py25 は特殊な virtualenv 環境を作って実行
  • py26 以降は通常の virtualenv 環境を作って実行

という風に環境を切り分けているため、tox.ini の envlist には python 2.6 以降のバージョンのみ表記しています。

[tox]
envlist=py26,py27,py32,py33

各テストツール群の古い python への対応状況

いくつかのテストツールは古い python での挙動が怪しくなっています。

mock ライブラリ

mock ライブラリは 1.0.0 で python 2.4 のサポートを終了しました。
python 2.4 で mock ライブラリを利用する場合は 0.8.0 を指定して利用する必要があります。

flake8

flake8 は python 2.4 では動作しません。
changelog では一言も触れられていないので、過去のバージョンでも動作しない可能性が高いです。

nose

僕はテストランナーとして nose を利用していますが、nosetests コマンドは python 2.4 でも動作します。

しかし、例外ハンドリングで python 2.5 から導入された BaseException を使っているようで、
エラーが出た時に stack trace が上書きされてしまうという問題がありました。

E....
======================================================================
ERROR: Failure: ImportError (No module named mock)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/ubuntu/src/bitbucket.org/tk0miya/diff-highlight/.tox/py24/lib/python2.4/site-packages/nose/failure.py", line 37, in runTest
    if isinstance(self.exc_val, BaseException):
NameError: global name 'BaseException' is not defined

----------------------------------------------------------------------
Ran 5 tests in 0.251s

FAILED (errors=1)

エラーの内容そのもの(ImportError)は表示されるので、その内容をヒントに追うことになります。

unittest / unittest2

python 標準の unittest は python 2.7 で (python 3.x の変更がバックポートされ)、
いくつか assert メソッドが増えているなど、使い勝手が向上しています。

python 2.6 以前向けに unittest2 パッケージが公開されているので、
これを利用してテストを書くとよいでしょう。

僕はほぼすべてのテストで、以下のコードが先頭に書かれています。

import sys

if sys.version_info < (2, 7):
    import unittest2 as unittest
else:
    import unittest

ただし、python 2.4 や python 2.5 を対象とする場合、
unittest2 のすべての機能を使い尽くすことができません。
ハマったところを列挙しておきます。

  • python 2.4 には with 文がないので、with self.assertRaises(...): が使えない
  • python 2.4/2.5 にはクラスデコレータがないので、クラスに対する @skipIf が使えない

また、unittest, unittest2 がらみの失敗としては、unittest と unittest2 を混在させると死を見ます。
(参考: python 2.6: 僕と unittest と時々 unittest2)

six

訂正 2014/1/6: six に関する情報を追記


試してみたところ、setup.py を実行する段階でコケるようになっています。

というわけで、1.4.1 を使いましょう。

まとめ

各種ツール、ライブラリの動向を追うと、徐々に過去の python のサポートが終了しています。
今回問題となった setuptools や virualenv、tox などでは古い python コードの開発が難しくなっています。

しかし、RedHat 系では長らく python 2.4 が使われていた事情もあり、
現在稼働中の OS では古い python が動いているという現状もあります。

今回、python 2.4 に対応するためにいろいろまとめてみた限り、
うまく条件を整えれば python 2.4-3.3 でコードベースをひとつにして、まともなテスト環境を作ることができました。
今回の悪戦苦闘の成果はリポジトリdrone.io の設定に反映されているので、
興味がある方はこちらも覗いてみてください。つらいです。

とはいえ、バージョン間で構文や振る舞いが変わっているところも多いため、
過去の python のサポートは開発者の無理のない程度になるといいなあと思うのでした。