Python: Mac OS X での locale.getpreferredencoding()

2007/07/19 10:30am

(追記)この記事に載せているパッチよりも Apple によるロケール周りの変更点を Python 2.5.1 にも適用するのパッチを使った方がよさそうです。Mac OS X 標準の Python との互換性があります。

TracInstall に従って、Mac OS X で Trac を動かそうとしている。以前試したときは頓挫したのだが、今回は Python を含むすべてのライブラリを一からインストールして、じっくりやってみることにした。

とりあえず、tracd を起動するところまでは問題なくいったのだけれど、URL にアクセスしてみると早速こんなエラーが。

Traceback (most recent call last):
  File "/path/to/trac/lib/python2.5/site-packages/trac/web/main.py", line 406, in dispatch_request
    dispatcher.dispatch(req)
  File "/path/to/trac/lib/python2.5/site-packages/trac/web/main.py", line 207, in dispatch
    populate_hdf(req.hdf, self.env, req)
  File "/path/to/trac/lib/python2.5/site-packages/trac/web/main.py", line 77, in populate_hdf
    'time': format_datetime(),
  File "/path/to/trac/lib/python2.5/site-packages/trac/util/datefmt.py", line 65, in format_datetime
    return unicode(text, encoding, 'replace')
LookupError: unknown encoding: X-MAC-JAPANESE

じっくりやってみる、と書いた矢先にくじけそうになりましたが、諦めずに原因を追ってみますね。

その前に、今回試したバージョンは、

です。

ソースコードを読む

スタックトレースから例外の発生箇所は簡単に分かるので、ソースコードを読んでみる (/trac/util/datefmt.py) 。

def format_datetime(t=None, format='%x %X', gmt=False):
    if t is None:
        t = time.time()
    if not isinstance(t, (list, tuple, time.struct_time)):
        if gmt:
            t = time.gmtime(float(t))
        else:
            t = time.localtime(float(t))

    text = time.strftime(format, t)
    encoding = locale.getpreferredencoding() or sys.getdefaultencoding()
    if sys.platform != 'win32':
        encoding = locale.getlocale(locale.LC_TIME)[1] or encoding
        # the above is broken on win32, e.g. we'd get '437' instead of 'cp437'
    return unicode(text, encoding, 'replace')

今回のエラーはビルトイン関数の unicodeX-MAC-JAPANESE エンコーディングをサポートしていないのが原因のようだ。X-MAC-JAPANESE は Mac ユーザにとってはお馴染みの MacJapanese(ほぼ Shift-JIS)のことだろう。たしかにリファレンスの 4.8.3 Standard Encodings を読んでもそれらしきエンコーディングがない。

となると、考えられる解決方法はふたつ

  1. MacJapanese をサポートするコーデックを追加
  2. そもそも、locale.getpreferredencoding() でサポートしていないエンコーディングを返さない。この場合は shift_jis を返してしまっていい気がする

まあ、1. が真っ当なんだろうけど、2. でも問題ないというか、むしろ、getpreferredencoding という名前のメソッドがサポートされていないエンコーディングを返すよりいいんじゃないだろうか。

_localmodule.c を変更

とりあえず、Python 2.5.1 の Modules/_localemodule.c で、X-MAC-JAPANESE の場合は shift_jis を返すように変更してみた。

--- Modules/_localemodule.c.orig        2007-07-19 02:47:21.000000000 +0900
+++ Modules/_localemodule.c     2007-07-19 03:17:28.000000000 +0900
@@ -426,6 +426,7 @@
     case kCFStringEncodingMacCyrillic: return "mac-cyrillic";
     case kCFStringEncodingMacTurkish: return "mac-turkish";
     case kCFStringEncodingMacIcelandic: return "mac-icelandic";
+    case kCFStringEncodingMacJapanese: return "shift_jis";
     /* XXX which one is mac-latin2? */
     }
     if (!name) {

ちなみに、このへんのコードでは Mac OS X のフレームワークである Core Foundation が利用されている。