sourceforge.net で Trac が動く

動くもんだなーと。ClearSilver (うっかり Silverlight と書きそうになった) だけ CentOS 4.4 でビルドしたのを持ち込み。カラオケいく前にコンビニでつまみを買った、昔のあの罪悪感に似ている。

ハマりポイントは

  1. sf.net の web サーバに入っている Python モジュールの MySQLdb が connection encoding の判定に mysql_character_set_name() を使っていること。
  2. sf.net のシェルサーバの libmysqlclient (proto ver=10) が mysql CLI フロントエンド (proto ver=14) の libmysqlclient と異なっており、かつ前者が multibyte charset 対応にビルドされていないこと。
  3. Python の default character encoding は sys.setdefaultencoding() で設定できるが、これは site モジュールにより起動時に無効にされること。
  4. mod_rewrite のルールと認証 (basic 認証のみ対応)

1. について、当該関数はサーバ側に問い合わせることをせず、クライアント側のコンテキストの文字コードを返すようにしている。なので SET NAMES などのクエリを発行してもこいつは更新されず、connection encoding がいつまでも latin1 とみなされ、クエリ生成時の変換に失敗する。どうせ UTF-8 しかやりとりされないので、MySQLdb.Connection の conversions リストに types.UnicodeType 対応の UTF-8 決め打ちコンバータを登録する。
2. については、不正なマルチバイト文字列が DB 接続に紛れ込む余地はないので (一旦 decode -> encode するので) みなし latin1 でエスケープしても便宜上問題ない。
3. は import sys したのちに reload(sys) すれば生き返るのでもれなく utf-8 と設定すれば OK。

const char * STDCALL mysql_character_set_name(MYSQL *mysql)
{
  return mysql->charset->csname;
}

追記
いくらか興味を持っていただいているようなので。まず、バージョン0.10.4を使いましたが、バックエンドの種類ではまったことはなかったです。MyISAM で問題なく動いています。

文字セットの問題ですが、上には詳細に記述しなかったものの、パッチを当てました。

下記のパッチは 1., と 3. についての修正を含んでいます。

--- trac-0.10.4.orig/trac/db/mysql_backend.py	2007-04-20 22:41:46.000000000 +0900
+++ trac-0.10.4/trac/db/mysql_backend.py	2007-06-29 15:27:36.000000000 +0900
@@ -125,6 +125,8 @@
             raise TracError, 'MySQL servers older than 4.1 are not supported!'
         cnx.query('SET NAMES %s' % charset)
         cnx.store_result()
+        cnx.query('SET CHARACTER SET %s' % charset)
+        cnx.store_result()
         cnx.charset = charset
 
     def __init__(self, path, user=None, password=None, host=None,
@@ -145,10 +147,18 @@
         if (self._mysqldb_gt_or_eq((1, 2, 1))):
             cnx = MySQLdb.connect(db=path, user=user, passwd=password,
                                   host=host, port=port, charset='utf8')
+        elif (self._mysqldb_gt_or_eq((1, 1, 0))):
+            cnx = MySQLdb.connect(db=path, user=user, passwd=password,
+                                  host=host, port=port, use_unicode = True,
+                                  conv = conv)
+            self._set_character_set(cnx, 'utf8')
         else:
             cnx = MySQLdb.connect(db=path, user=user, passwd=password,
-                                  host=host, port=port, use_unicode=True)
+                                  host=host, port=port, unicode = 'utf8')
             self._set_character_set(cnx, 'utf8')
+            import types
+            cnx.converter[types.UnicodeType] = lambda u, d: cnx.literal(
+                u.encode('utf8'))
         ConnectionWrapper.__init__(self, cnx)
         self._is_closed = False
 
--- trac-0.10.4.orig/cgi-bin/trac.cgi	2007-10-24 00:42:52.000000000 +0900
+++ trac-0.10.4/cgi-bin/trac.cgi	2007-06-29 16:40:59.000000000 +0900
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #
 # Copyright (C) 2003-2004 Edgewall Software
@@ -16,6 +16,9 @@
 # Author: Jonas Borgström <jonas@edgewall.com>
 
 try:
+    import sys
+    reload(sys)
+    sys.setdefaultencoding('utf-8')
     from trac.web import cgi_frontend
     cgi_frontend.run()
 except SystemExit:

また、trac.cgi として下記のようなラッパを使っています。

#!/bin/sh
GROUP_HOME=`readlink -f \`dirname "$0"\`/..`
TRAC_PREFIX="$GROUP_HOME/opt/trac"
CLEARSILVER_PREFIX="$GROUP_HOME/opt/clearsilver"
DOCUTILS_PREFIX="$GROUP_HOME/opt/docutils"
SETUPTOOLS_PREFIX="$GROUP_HOME/opt/setuptools"
PYTHONPATH=`echo \$TRAC_PREFIX/lib/python*/site-packages`
PYTHONPATH=$PYTHONPATH:`echo \$CLEARSILVER_PREFIX/lib/python*/site-packages`
PYTHONPATH=$PYTHONPATH:`echo \$DOCUTILS_PREFIX/lib/python*/site-packages`
PYTHONPATH=$PYTHONPATH:`echo \$SETUPTOOLS_PREFIX/lib/python*/site-packages`
TRAC_ENV="$GROUP_HOME/var/opt/trac"
export PYTHONPATH
export TRAC_ENV
exec $TRAC_PREFIX/share/trac/cgi-bin/trac.cgi

ちなみにディレクトリ構成は

$GROUP_HOME
  |
  +- var
  |  |
  |  +- opt
  |     |
  |     +- trac
  |        |
  |        +- attachments
  |        |
  |        +- conf
  |        |
  |        +- htdocs ($GROUP_HOME/htdocs/chrome/site への symlink)
  |        |
  |        +- log
  |        |
  |        +- plugins
  |        |
  |        +- templates
  |        |
  |        +- wiki-macros
  |
  +- opt
     |
     +- clearsilver-0.10.4
     |
     +- clearsilver (./clearsilver-0.10.4 への symlink)
     |
     +- docutils-0.4
     |
     +- docutil (./docutil-0.4 への symlink)
     |
     +- setuptools-0.6c7
     |
     +- setuptools (./setuptools-0.6c7 への symlink)
     |
     +- trac-0.10.4
     |
     +- trac (./trac-0.10.4 への symlink)

上記のようになっています。