2.4と2.5の差異?

家にあるPython2.5 on Fedora9で

import sys
import gettext
from optparse import OptionParser
t=gettext.translation('hogehoge',)
_=t.ugettext
parser=OptionParser()
parser.add_option("-e","--exclude",dest="exclude_expr",action="append",
            metavar="EXPR",help=_("regular expression for exclude file"))
parser.print_help()

とすると期待通りにローカライズされた表示が出来る。が、研究室にあるPython2.4 on CentOS5.2だとUnicodeDecodeErrorが出る。
ugettextでなくlgettextを使ってやるとPython2.4では問題なくなるが、Python2.5で問題が出る。

Python2.5のoptparseでは"print_help()"で

if file is None:
    file = sys.stdout
encoding = self._get_encoding(file)
file.write(self.format_help().encode(encoding, "replace"))

としているのだが"encode()"があるので"self.format_help()"はユニコード文字列を返すことを期待されている。しかしながらlgettextが返すのはただの文字列なので、エラーが発生するようだ。*1推定されるエラー発生までの処理は以下のとおり;

  1. ユニコード文字列ではないのでユニコード文字列にしよう
  2. sys.getdefaultencoding()で返ってきたのは'ascii'
  3. じゃあこの文字列は'ascii'だと仮定してユニコード文字列にしよう
  4. 'ascii'の範囲外の文字列に遭遇した
  5. エラー

"locale.getpreferredencoding()"は'UTF-8'を返すのに何故か"sys.getdefaultencoding()"は'ascii'を返すのに納得いかないがまあユニコード文字列とただの文字列の使い分けをしっかりすれば問題ないので気にしないでおく。

Python2.4だと逆の結果になるのは"print_help()"で"encode()"を使っていないためのようだ。Python2.4における"print_help()"は以下のようになっている。

if file is None:
    file = sys.stdout
file.write(self.format_help())

よってとりあえずadhocに

if (int(sys.version[0])*10+int(sys.version[2]))>24:
    _=t.ugettext
else:
    _=t.lgettext

としてみた。

*1:ここで言うユニコード文字列とただの文字列は一般で言う意味と若干異なるPython独自の区別をさす。ユニコード文字列はISO 10646でエンコードされた文字列をさす。ただの文字列の場合は何でエンコードされているかは文字列しだい。文字コードがわかっているならば"decode()"でユニコード文字列に変換できる。逆にユニコード文字列は"encode()"で任意の文字コードエンコードした文字列に変換できる。