python3/mod_wsgiで日本語表示

python3.1がリリースされたのでmod_wsgiを使ってwebサービスが書けないかと模索したときの記録。
主に日本語を表示するために行った作業の備忘として記述する。

26th Oct. 2009

後述の内容は[Web-SIG]メーリングリストで2007年12月の段階で議論されていたようです。
まだ詳しくは読んでいませんので結論がどうなっているかはよくわかりません(^^;

動作環境

Amazon EC2 smallインスタンス
fedora8
apache 2.2.9

LANG=ja_jp.utf-8

python3のインストール

作業日(26th Oct. 2009)現在、最新版は 3.1。
PyJUGページでは3系では 3.0.1までしかおいてなかったので、python.orgのダウンロードページより3.1.1をダウンロード
展開して、configure/make/make install

tar zxvf Python-3.1.1.tgz
cd Python-3.1.1
./configure
make
su -
make install

ただ、sqlite3のライブラリが入っていなくて configureで怒られたのでyumでインストールした。

なお、fedora8ではpython2.5がインストール済みでいろいろな場面で利用されるので、これを上書きしないようにpython3をインストールする。
幸いにもpythonのconfigureがこれを理解しており『python3.1』および『python3』という名前でインストールされるので、上記環境であればあまり意識することはない。

mod_wsigのインストール

作業日(26th Oct. 2009)現在、最新版は 3.0c5。
ただし、このバージョンはトップページでは紹介されていない。ダウンロードページにいくと存在している。

これも適当に展開。
configure時にpython3.1を明示的に指定して、make/make install

tar zxvf mod_wsgi-3.0c5.tar.gz
cd mod_wsgi-3.0c5
./configure --with-python=/usr/local/bin/python3.1    <--- ここでpython3.1を明示的に指定すること
make
su -
make install

動作設定

httpd.confに

LoadModule wsgi_module modules/mod_wsgi.so
WSGIScriptAlias /wsgi/test "/var/www/wsgi/test.py"
<Location "/wsgi/test">
    WSGIApplicationGroup %{GLOBAL}
    Order deny,allow
    Allow from all
</Location>

を追加。
これで

 http://ドメイン名/wsgi/test


/var/www/wsgi/test.py

のpythonスクリプトが実行されるようになる。
これは1つのファイルで複数のリクエストを受けるようないまどきのフレームワークの記述ができる。

もし、複数のpythonスクリプトファイルを任意に実行したいのであれば

WSGIScriptAlias /wsgi/ "/var/www/wsgi/"
<Location "/wsgi">
    WSGIApplicationGroup %{GLOBAL}
    Order deny,allow
    Allow from all
</Location>

のように記述すればよい。
これで

 http://ドメイン名/wsgi/

以下のファイルはすべてpythonスクリプトだと解釈されるはず。(実は確認してない^^;)

wsgi向けのスクリプトファイル

mod_wsgiでうごかすスクリプトファイルは単純。
もっとも単純なスクリプト

/var/www/wsgi/test.py

def application(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    return 'Hello world'

これで

http://ドメイン名/wsgi/test

とアクセスすると

Hello world

と表示される。

日本語を表示してみる --- できない orz

ここまでくれば問題なし、、、とおもい、文字列に日本語をいれてみた。

/var/www/wsgi/test.py

def application(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    return 'こんにちは 世界'

で、ページを表示....

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator, ohneta and inform them of the time the error occurred, and anything you might have done that may have caused the error.
More information about this error may be available in the server error log.

......orz

apacheのエラーログをみると

[Tue Oct 27 07:36:01 2009] [error] [client 58.95.84.110] mod_wsgi (pid=19164): Exception occurred processing WSGI script '/var/www/wsgi/test.py'., referer: http://ドメイン名/index.html
[Tue Oct 27 07:36:01 2009] [error] [client 58.95.84.110] TypeError: sequence of byte string values expected, value containing non 'latin-1' characters found, referer: http://ドメイン名/index.html

とのこと。

なにか設定が必要なのか?とおもい、いろいろやってみたがまったく歯が立たず。
pythonのインタプリタ環境ではUTF-8な日本語は普通に使えるので、mod_wsgiに疑念の目を向ける。

mod_wsgiのソースコード(mod_wsgi.c)を読んでみて、エラーログのメッセージから問題がでていると思われる場所は特定。
3960行目前後の関数Adapter_write()あたりだと思われる。

static PyObject *Adapter_write(AdapterObject *self, PyObject *args)
{
    PyObject *item = NULL;
    const char *data = NULL;
    int length = 0;

    if (!self->r) {
        PyErr_SetString(PyExc_RuntimeError, "request object has expired");
        return NULL;
    }

    if (!PyArg_ParseTuple(args, "O:write", &item))
        return NULL;

#if PY_MAJOR_VERSION >= 3
    if (PyUnicode_Check(item)) {
        PyObject *latin_item;
        latin_item = PyUnicode_AsLatin1String(item);
        if (!latin_item) {
            PyErr_Format(PyExc_TypeError, "byte string value expected, "             ←ここらへん
                         "value containing non 'latin-1' characters found");
            Py_DECREF(item);
            return NULL;
        }

        Py_DECREF(item);
        item = latin_item;
    }
#endif

    if (!PyString_Check(item)) {
        PyErr_Format(PyExc_TypeError, "byte string value expected, value "
                     "of type %.200s found", item->ob_type->tp_name);
        Py_DECREF(item);
        return NULL;
    }

    data = PyString_AsString(item);
    length = PyString_Size(item);

    if (!Adapter_output(self, data, length, 1))
        return NULL;

    Py_INCREF(Py_None);
    return Py_None;
}

python3以上の場合、表示されるべき文字列を変数itemで取得し、unicodeだったならばLatin-1に文字コードを変換しているようである。
これ、だめっしょ。。。UTF-8なコードをすべてLatin-1には変換できないでしょ。
元のコードがなんでこのようにしているのか解読できないのだが、

PyUnicode_AsLatin1String(item);

を全部、

PyUnicode_AsUTF8String(item);

に書き換えて再度make/make installしてhttpdを再起動。
で、ページ表示させると、

こんにちは 世界

OK、OK。
これで日本語を含む文字列でも正常に表示されるようになった。


なぜこんな問題が出ているのかよくわからないが、本家が対応してくれるまでは、これでしのごうとおもう。
まだ、本格的にpython3+mod_wsgiでwebサイトを作りはじめたわけではないので、いまのところは私にはこれで十分です。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-06-13 (月) 17:24:59 (915d)