mod_python マニュアル リリース 3.1.3 Gregory Trubetskoy (和訳: 日本 Python ユーザ会) 平成 17 年 9 月 21 日 E-mail: [email protected] Copyright © 2004 Apache Software Foundation. Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 概要 mod_python を使って Apache サーバに Python を埋め込むと、大幅に速度を改善しつつ柔軟に Web ベー スアプリケーションを設計できるようになります。このドキュメントは mod_python の網羅的なリファレ ンスに加え、ユーザガイドやチュートリアルとしても使える必要かつ的確な情報源を目指して書かれてい ます。 参考資料: プログラミング言語 Python の Web サイト (http://www.python.org/) プログラミング言語 Python に関する情報 Apache Server Web サイト (http://httpd.apache.org/) Apache HTTP サーバに関する情報 目次 第 1 章 はじめに 1.1 1.2 1.3 パフォーマンス . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 柔軟性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 歴史 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 第 2 章 インストール 2.1 2.2 2.3 2.4 2.5 前提条件 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . コンパイル . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . インストール . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . テスト . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . トラブルシューティング . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 第 3 章 チュートリアル 3.1 3.2 3.3 3.4 publisher ハンドラを使ってみる . . . . Apache リクエスト処理の概要 . . . . . mod_python は一体何をやっているのか 今度はもっと複雑なものを - 認証 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 第 4 章 Python API 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 . . . . 47 47 51 52 52 フィルタハンドラの概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 接続ハンドラの概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 第 5 章 Apache 設定ディレクティブ 5.1 5.2 5.3 5.4 Request Handlers . . . . フィルタ . . . . . . . . 接続ハンドラ . . . . . . その他のディレクティブ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 第 6 章 標準のハンドラ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 9 9 11 . . . . . リクエストハンドラの概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 5 6 15 15 15 18 19 20 33 36 39 42 複数のインタプリタ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . apache – Apache 内部へのアクセス util – 雑多なユーティリティ . . . Cookie – HTTP 状態管理 . . . . . . Session – セッション管理 . . . . . psp – Python Server Pages . . . . . . 1 1 1 2 57 i 6.1 6.2 6.3 publisher ハンドラ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 PSP ハンドラ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 CGI ハンドラ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 付 録 A バージョン 2.x からの変更 63 付 録 B 日本語訳について 65 索引 67 ii 一章 はじめに 1.1 パフォーマンス mod_python を使う大きな利点の一つに,従来の CGI に対する大幅な速度向上があります.以下に示すの はかなり大雑把なテストの結果です.このテストは Red Had Linux 7.3 の動作している 1.2 GHz の Pentium マシン上で行いました.これら 4 種類のスクリプトの呼び出しは Ab を使って行い,いずれのスクリプト も標準モジュールの cgi を呼び出し (通常の Python CGI スクリプトはみな cgi の import で始めるからで す),その後 ‘Hello!’ を出力するようにしています.結果は一度に一つづつ,10000 回のリクエストを処 理させたときの結果に基づいた値です. 標準の CGI: Mod_python cgihandler: Mod_python publisher: Mod_python handler: 1.2 23 requests/s 385 requests/s 476 requests/s 1203 requests/s 柔軟性 Apache は処理を (リクエストの読み出し,ヘッダの解析,アクセス権限のチェックといった) フェイズに分割 します.これらのフェイズはハンドラ (handler) と呼ばれる関数で実装できます.伝統的には,ハンドラは C で書いて Apache モジュールにコンパイルします.これに対して, mod_python では Python で Apache ハンドラを書いて,Apache の機能を拡張できるのです. Apache のリクエスト処理過程についての詳しい 情報は Apache API ノート や,mod_python - Integrating Python with Apache といった論文を参照してくだ さい. CGI から移行しやすくするために,標準の mod_python ハンドラが提供されていて,CGI 環境をシミュ レートし,ユーザが古いスクリプトのコードを (たいていは) ほとんど変更しなくても mod_python 下で 実行できるようになっています. 参考資料: http://dev.apache.org/ Apache Developer Resources http://www.modpython.org/python10/ Mod_Python - Integrating Python with Apache, presented at Python 10 1 1.3 歴史 mod_python は,もともと Httpdapy というプロジェクトに端を発しています.長い間, Httddapy が mod_python と呼ばれることはありませんでしたが,これは Httpdapy が Apache 固有のものを目指し てはいなかったからです.Httpdapy はクロスプラットフォームになるよう設計されていて,実際最初に書 かれた Httpdapy は Netscape サーバ向けでした (当時は Nsapy と呼ばれていました. 1997 年). 以下に引用した Httpdapy の README ファイルは,当時の課題と,HTTP サーバへの Python 埋め込みが もたらす解決策をよく解説しています: 数年前,初めて WWW アプリケーションを開発しているときに,(商用のリレーショナルデー タベースであれ,それ以外であれ) RDB に接続する必要のあるプログラムを CGI で組むと非 常に低速になるということがわかりました.というのも,検索を行うたびに数メガバイトもの サイズのインタプリタの実行形式がロードされるし,データベースライブラリ自体も大きくて, おまけにデータベースへの接続と認証のプロセスは DNS の名前解決や暗号化,メモリ確保... といった処理が入るために,オーバヘッドが非常に大きくなるからです. アプリケーションの高速化に迫られて,私は Python を使い続けるのをほとんどあきらめ,WWW とデータベースの統合に特化していると謳ったツールを探しはじめました. MS の ASP を信 頼する気には全くなれませんでしたし,Netscape の LiveWire の低速さとバグの多さにはイラ イラしました.Cold Fusion は有望でしたが,すぐに html ライクなタグを書くのはプログラム の可読性をアセンブラ並みにすると気づきました.PHP についても同じです.何よりも,私は Python で書きたいと *心の底から* 願っていたのです. ちょうどそのころ, Internet Programming With Python という本が出版され,Netscape サーバに Python を埋め込む方法を解説した章に惹かれました.その例題を自分のプロジェクトに使って みて,後に Nsapy と呼ばれる, Windows NT と Solaris の両方でコンパイルできる改良版を開 発したのです.Naspy は Netscape サーバでしか動きませんでしたが,Naspy は Python の精神 に適うとても洗練された汎用のオブジェクト指向設計だったため,他の Web サーバに容易に移 植できました. 折りしも Netscape 製のサーバは人気を失いつつありました.そこで Nsapy を他のサーバに移 植することにしました.手始めにもっとも人気のあった Apache に移植したのです.かくして, Nsapy から Httpdapy が誕生したのです. ... この話には続きがあります.正直なところ,全てのサーバ向けに Httpdapy を書くのは,最初に考えて いたよりもちょっと大きな仕事になるし,さして興味も沸かなかったのです. それよりも,人気の高い Perl の Apache 拡張である mod_perl と同じような (あるいはそれ以上の) 機能を もたらすような Python の Apache 拡張を作るのはとてもワクワクするできごとに思えたのです. こうして mod_python はできました.最初の mod_python をリリースしたのは 2000 年の 3 月でした. 2 二章 インストール 注意: インストールやその他の問題に関して参考になるのは断然 mod_python メーリングリストです。表題 に ‘subscribe’ と書いたメールを mod_python メーリングリストに [email protected] に送って、 mod_python メーリングリストに参加しましょう。 2.1 前提条件 • Python 2.2.1 またはそれ以降のバージョン。それ以前のバージョンでは動作しないはずです。 • Apache 2.0.40 またはそれ以降のバージョン (Apache 1.3.x, 用には、mod_python version 2.7.x を使っ てください)。 mod_python をコンパイルするには、Apache と Python の両方の include ファイルと、Python のライブ ラリがシステムにインストールされていなければなりません。Python と Apache をソースコードからインス トールしたなら、必要なものはすべて揃っているはずです。しかしパッケージ済みのソフトウェア (Red Hat Linux の RPM や Debian、 sunsite にある Solaris のパッケージなど) を使った場合、バイナリしかインス トールされず、ソースコードが入っていないかもしれません。mod_python のコンパイルに必要な Apache や Python の include ファイルやライブラリが別に「開発者むけ (development)」 パッケージになっている こともよくあります。必要なファイルがすべて揃っているかどうか分からなければ、 Python や Apache を ソースコードからコンパイルするか、自分の使っているシステムのドキュメントをよく読んで、開発者む けパッケージを入手する方法を調べてください。 2.2 コンパイル モジュールをコンパイルして Apache にリンクする方法には、静的な方法と DSO (Dynamic Shared Object) の二つがあります。 最近では DSO の方が多く使われており、mod_python でも推奨する方法です。DSO モジュールは共有 ライブラリとしてコンパイルされ、サーバから実行時にロードされます。 DSO の利点は、Apache 本体を再コンパイルしなくてもインストールでき、必要に応じて利用できると ころにあります。 Apache DSO メカニズムの詳しい解説は http://httpd.apache.org/docs-2.0/ dso.html で入手できます。 現在 mod_python がサポートしているのは DSO だけです。 静的 (static) リンクは古いアプローチです。ほとんどのプラットフォームで動的リンクを行えるように なっているので、静的リンクはますます利用されなくなっています。静的リンクの大きな欠点は Apache を 再コンパイルしなければならず、それはほとんどの場合では好ましくない選択肢であるということです。 3 2.2.1 ./configure を実行する ./configure スクリプトを実行すると、実行環境を解析して、使っているシステムに特化した専用の Makefile を作成します。autoconf が実行する標準の処理以外に、./configure は以下の処理を実行します: • apxs というプログラムが利用可能かどうかを調べます。apxs は標準の Apache 配布物に入っていて、 DSO のコンパイルに必要です。PATH や/usr/local/apache/bin の下に apxs が見つからなかった場合、 DSO のコンパイルは実行できません。 以下のようにして--with-apxs オプションを使うと apxs のありかを手動で指定できます: $ ./configure --with-apxs=/usr/local/apache/bin/apxs このオプションは指定しておきましょう。 • Python のバージョンを調べ、Python バイナリ中にコンパイルされている様々なパラメタから、libpython がどこにあるかを判定しようとします。デフォルトでは PATH 中に見付かった python を使います。 PATH 内に最初に見付かる Python バイナリが mod_python の実行にふさわしくないバージョンであ るか、あるいは他のバージョンの Python を使いたい場合には、以下の例のように--with-python を指 定して、別の Python の在処を指定できます: $ ./configure --with-python=/usr/local/bin/python2.2 2.2.2 make を実行する • ビルドプロセスを開始するには、 $ make とするだけです。 インストール 2.3 2.3.1 make install の実行 • インストール作業のこの部分は root で行う必要があります。 $ su # make install – 上の作業で、Apache のすべてのモジュールが入る libexec ディレクトリにライブラリをコピー します。 – 最後に、Python ライブラリを site-packages にコピーし、コンパイルします。 4 NB: Python ライブラリだけ、あるいは DSO だけを選択的にインストールしたい場合 (この場合は常 にスーパユーザ権限が必要なわけではありません)、install_py_lib や install_dso といった make ター ゲットを使えます。 2.3.2 Apache の設定 • mod_python を DSO としてコンパイルした場合、Apache の設定ファイルに以下の設定行を追加し て、モジュールをロードするよう指示する必要があります。設定ファイルは通常 httpd.conf または apache.conf です: LoadModule python_module libexec/mod_python.so mod_python.so の実際のパスは場合によって異なりますが、make install を行うと mod_python.so が どこに置かれたか、そして LoadModule ディレクトリをどう書けばよいか的確に報告してきます。 2.4 テスト 1. 手元のウェブサイトから見えるディレクトリ、例えば ‘htdocs/test’ を作成してください。 2. 下記の Apache ディレクティブを、メインのサーバ設定ファイルか.htaccess のどちらかの中に追加 します。.htaccess ファイルを使うのなら、<Directory> タグは必要ありません (ディレクトリは .htaccess ファイルの置かれている場所になります) また、該当するディレクトリに対して少なくとも FileInfo の指定された AllowOverride ディレクティブを適用しておかねばなりません (デフォ ルトの設定は None で、動作しません)。 <Directory /some/directory/htdocs/test> AddHandler mod_python .py PythonHandler mptest PythonDebug On </Directory> (/some/directory は自分の環境に合わせて変更してください。通常は Apache の ServerRoot の値になり ます。) 3. ここまでの作業でメインの設定ファイルに変更を加えているならば、変更内容を有効にするために Apache を再起動する必要があります。 4. htdocs/test ディレクトリ下の mptest.py ファイルを編集して、以下のような内容にします (ブラウザ からカット& ペーストするときには注意しましょう。インデントが正しくなかったり、文法エラーに なったりするかもしれません): from mod_python import apache def handler(req): req.write("Hello World!") return apache.OK 5 5. mptest.py が参照先になるように、ブラウザに URL を指定します; ‘Hello World!’ という文字列を 読めるはずです。うまく読めなければ、次のトラブルシューティングの節を参照してください。 6. 全てうまく動作しているなら、3 節のチュートリアル に進みましょう。 2.5 トラブルシューティング 問題の原因を調べるためにできることがいくつかあります: • エラー出力が出ていれば、注意深く調べます。 • サーバのエラーログファイルを調べます。エラーログファイルには有用な手がかりが記録されている ことがあります。 • Apache をコマンドラインから単一プロセスモード (single process mode) で起動してみましょう: ./httpd -X この起動方法は Apache をバックグラウンドで動作しないようにするので、有用な情報を得られるこ とがあります。 • mod_python メーリングリストで質問してみましょう。質問するときには、 – mod_python のバージョン。 – オペレーティングシステムのタイプ、名前とバージョン。 – Python のバージョンと、コンパイル時に特別に指定したオプションがあればそれも。 – Apache のバージョン。 – Apache 設定ファイルや .htaccess 中の関連する部分。 – Python コードの関連する部分。 といったスペックを載せるようにしてください。 6 三章 チュートリアル で、どうやったらちゃんと動くんですか? この章は、mod_python をインストールした後で mod_python プログラミングを始めてみる人のため のガイドです。インストールマニュアルでは ありません! この章を読んだ後には、4, Python API 以降の節 (すくなくとも冒頭の部分) も読んでおくよう強く勧め ます。 3.1 publisher ハンドラを使ってみる この節では、mod_python の詳細に深くつっこまなくても使い始められる publisher ハンドラの概要につ いて手短に説明します。mod_python ハンドラの動作する仕組みや、ハンドラとは一体何なのかについて は、チュートリアル以後の節で詳しく説明します。 publisher ハンドラは mod_python の標準ハンドラの一つとして提供されています。このハンドラを 使えるようにするには、設定ファイルに以下のように書く必要があります: AddHandler mod_python .py PythonHandler mod_python.publisher PythonDebug On さて、下記はフィードバックを送信する簡単なフォームの例です。このフォームは名前、電子メールア ドレス、そしてコメントをユーザに入力してもらい、その情報を元にウェブ管理者宛の電子メールを生成 します。このシンプルなアプリケーションは、form.html - データを集めるためのフォーム、そして form.py - フォームのアクション時のターゲット、という二つのファイルからなります: フォームの HTML ソースを以下に示します: <html> フィードバックを入力して送ってください: <p> <form action="form.py/email" method="POST"> Name: <input type="text" name="name"><br> Email: <input type="text" name="email"><br> Comment: <textarea name="comment" rows=4 cols=20></textarea><br> <input type="submit"> </form> </html> <form> タグの action 要素が form.py/email を指していることに注意してください。次に以下の ような内容で form.py という名前のファイルを作成しましょう: 7 import smtplib WEBMASTER = "webmaster" # webmaster e-mail SMTP_SERVER = "localhost" # your SMTP server def email(req, name, email, comment): # ユーザが全ての情報を入力したか確かめる if not (name and email and comment): return "必要な情報が入力されていません。戻って正しく入力してください。" # メッセージテキストを作成する msg = """\ From: %s Subject: feedback To: %s コメントです: %s それでは。 %s """ % (email, WEBMASTER, comment, name) # 送信する conn = smtplib.SMTP(SMTP_SERVER) conn.sendmail(email, [WEBMASTER], msg) conn.quit() # ユーザにフィードバックの内容を返す s = """\ <html> %s さん<br> コメントありがとうございました。すぐにお返事いたします。 </html>""" % name return s ユーザが「送信 (Submit)」ボタンを♂と、publisher ハンドラはフォームの各フィールドの情報をキーワー ド引数にして form モジュール内の関数 email を呼び出します。また、request オブジェクトも req 引数 として渡します。 必要がなければ、req すら関数の引数にして渡す必要はありません。publisher ハンドラは賢くて、関数 が受け取れる引数だけを渡します。 データは関数の戻り値を介してブラウザに戻されます。 publisher ハンドラは、mod_python を使ったプログラム作成を大幅に単純化しながらも、request オブジェ クトにアクセスできるために mod_python の全てのパワーを引き出せるようになっています。publisher ハン ドラでは、 「ネイティブの」mod_python ハンドラを使ってできる全てのこと、例えば req.headers_out を介したカスタムヘッダを作成したり、apache.SERVER_ERROR 例外を送出してエラーを返したり、 req.write() や req.read() を使ってクライアントとの間で直接データを読み書きしたり、などと いった処理を行えます。 8 publisher ハンドラの詳しい情報は 6.1publisher ハンドラを参照してください。 3.2 Apache リクエスト処理の概要 mod_python の機能について深く調べてみたいのなら、ハンドラ (handler) とは何かを知っておく必要が あります。 Apache はリクエストを複数の フェイズ (phase) で処理します。例えば、最初のフェイズはユーザを認証 し、次のフェイズではユーザが特定のファイルを閲覧する許可を有しているか調べ、続いて (次のフェイズ で) ファイルを読み出してクライアントに送信する、といった具合です。典型的な静的ファイルに対する要 求には、(1) 要求された URI を実際のファイルの場所に変換する、 (2) ファイルを読み出してクライアント に送信する、 (3) リクエストをログに記録する、という三つのフェイズがあります。厳密にどのフェイズが どのように処理されるかは、設定によって大きく変わります。 ハンドラ (handler) は、一つのフェイズを処理する関数です。ある特定のフェイズの処理に対して、複数 のハンドラが利用できることもあり、この場合各ハンドラは Apache から順番に呼び出されます。各フェイ ズに対して、Apache のデフォルトのハンドラが呼び出され (デフォルトハンドラのほとんどはデフォルト で基本的な機能しかしないか、あるいは全く何も行いません)、続いて mod_python のような Apache モ ジュールで提供されている追加のハンドラが呼び出されます。 mod_python では、提供可能な全ての Apache ハンドラを提供しています。デフォルトの mod_python ハンドラは設定ディレクティブで特に設定しない限りなにも行いません。mod_python の設定ディレク ティブは (PythonAuthenHandler のように)‘Python’ で始まって ‘Handler’ で終わり、あるフェーズ をある Python 上の機能に関連づけています。従って、mod_python の主要な機能は Apache ハンドラとみ なさんのような開発者が書いた Python 関数との間を取り持つ役目ということになります。 もっともよく使われているハンドラは PythonHandler です。このハンドラでは、実際のコンテンツを 提供する、いわゆるリクエストのフェイズを処理します。このハンドラには特定の機能を表す名前がない ため、しばしば汎用 (generic) ハンドラと呼ばれます。このハンドラに相当するデフォルトの Apache の動 作は、ファイルの読み込みとクライアントへの送信です。自作のほとんどのアプリケーションではこのハ ンドラをオーバライドすることになるでしょう。利用できるハンドラ一覧を見るには、5節、Apache ディレ クティブ を参照してください。 3.3 mod_python は一体何をやっているのか 仮に、以下のように設定しているとしましょう: <Directory /mywebdir> AddHandler mod_python .py PythonHandler myscript PythonDebug On </Directory> 注意: /mywebdir は物理的な絶対パスです。 さらに、以下のような内容の Python プログラムが ‘/mywebdir/myscript.py’ にあるとします (Windows ユー ザは、ファイル名のスラッシュをバックスラッシュに置き換えてください): 9 from mod_python import apache def handler(req): req.content_type = "text/plain" req.write("Hello World!") return apache.OK すると,こんな処理が行われます: まず AddHandler ディレクティブが Apache に ‘/mywebdir’ またはそ れ以下のディレクトリにある ‘.py’ で終わる全てのファイルに対する全てのリクエストを mod_python で 処理するよう教えます。‘PythonHandler myscript’ ディレクティブは、mod_python が generic ハ ンドラでの処理を myscript スクリプトで行うように指示します。‘PythonDebug On’ でぃれくてぃぶ は、Python のエラーが発生した場合にエラー出力を (ログへの記録に加えて) クライアントに送信するよう mod_python に指示します。開発中には、この機能はとても便利です。 さて、リクエストが送られてくると、Apache は mod_python のハンドラを呼び出してリクエスト処理 フェイズを開始します。mod_python ハンドラは設定中にリクエストの内容に該当するハンドラを指定し ているディレクティブがないか探します (mod_python はディスパッチャのように働くことを思い出して ください)。ここで示した例題では、mod_python に generic ハンドラ以外のハンドラについて何もしてい ません。 generic ハンドラの番になると、mod_python は ‘PythonHandler myscript’ ディレクティ ブがあることに気づき、以下のような処理を行います: 1. PythonHandler ディレクティブを指定しているディレクトリが sys.path に追加されていなけれ ば追加します。 2. myscript という名前のモジュールを import しようと試みます。(myscript が PythonHandler ディレクティブの指定されているディレクトリのサブディレクトリにある場合、import はうまくい かないので注意してください。これはサブディレクトリが sys.path に入っていないからです。 ‘PythonHandler subdir.myscript’ のようなパッケージ表記を使えばこの問題を回避できま す。) 3. myscript 中に関数 handler がないか探します。 4. リクエスト (request) オブジェクトを渡して handler を呼び出します (request オブジェクトについて は後で詳しく述べます)。 5. この時点で、処理はスクリプトの中に移ります: この • from mod_python import apache 行で、Apache へのインタフェースを提供している apache モジュールを import します。数 少ない例外を除き、mod_python のプログラムには常にこの行があるはずです。 • def handler(req): ハンドラ (handler) 関数の宣言です。mod_python はディレクティブ名を小文字表記にして先 頭の ‘python’ を取り除いた名前をハンドラ名に使うため、この関数は ‘handler’ という名前 になるのです。ハンドラ関数に別の任意の名前を使って、‘::’ でディレクティブに明示的に 10 指定しても構いません。例えば、ハンドラが ‘spam’ という関数だったなら、ディレクティブは ‘PythonHandler myscript::spam’ になったでしょう。 ハンドラは引数を一つ - request オブジェクト - を取らねばなりません。request オブジェクトと は、例えばクライアントの IP アドレス、ヘッダ、 URI といった、あるリクエストに関する全て の情報の入ったオブジェクトです。クライアントへの返信も request オブジェクトを介して行い ます。従って、「応答オブジェクト (response object)」はありません。 • req.content_type = "text/plain" この行では、コンテントタイプを ‘text/plain’ に設定しています。通常、デフォルトでは ‘text/html’ を使いますが、ここで定義するハンドラが生成するのは HTML ではないので ‘text/plain’ の方が適切です。 • req.write("Hello World!") 文字列 ‘Hello World!’ をクライアントに書きます (説明するまでもありませんね?) • return apache.OK 全 て の 処 理 が う ま く い き 、リ ク エ ス ト が 処 理 さ れ た こ と を Apache に 教 え ま す。処 理 が う ま く 行 か な かった 場 合 に は 、こ の 行 で apache.HTTP_INTERNAL_SERVER_ERROR や apache.HTTP_FORBIDDEN を返すことになります。処理がうまく行かなかった場合、 Apache はエラーをログに記録して、クライアント向けのエラーメッセージを生成します。 熟 慮 す べ き こ と: 注 意 深 く 読 ん で い れ ば 、上 の テ キ ス ト で 、ハ ン ド ラ コ ー ド を 実 行 す る URL で myscript.py を 参 照 す る 必 要 が あ る と 書 い て い な い こ と に 気 づ い た で しょ う。必 要 な の は 、.py URL が ファイ ル を 参 照 し て い る と い う こ と だ け で す。実 際 、ファイ ル の 名 前 自 体 は 問 題 で は な く、URL が 指 し 示 し て い る ファイ ル が 実 在 し な く て も か ま わ な い の で す。従って 、上 の 設 定 の 下 で は 、‘http://myserver/mywebdir/myscript.py’ も ‘http://myserver/mywebdir/montypython.py’ も 同 じ 結 果 に な る の で す。こ こ で 理 解 し て おかねばならない要点は、ハンドラは特定のタイプのファイルを処理する際の動作を指定しているので あって、個々のファイルの動作を個別に指定しているのではないということです。 ために ここまで読み進んだ時点で上の文章が理解できなかった場合は、理解できるまで戻って何度も読み返し てください。 3.4 今度はもっと複雑なものを - 認証 基本的なハンドラの書き方を理解したところで、もう少し複雑なものに取り組んでみましょう。 ディレクトリをパスワードで保護したいとしましょう。ログイン名は ‘spam’、パスワードは ‘eggs’ に します。 ま ず、認 証 が 必 要 な 場 合 に 認 証 (authentication) ハ ン ド ラ を 呼 び 出 す よ う Apache に 教 え ま す。 PythonAuthenHandler を使います。従って、設定ファイルは以下のようになります: 11 <Directory /mywebdir> AddHandler mod_python .py PythonHandler myscript PythonAuthenHandler myscript PythonDebug On </Directory> 二つのハンドラに対して同じスクリプトが指定されていることに注意してください。ご存知のように、 mod_python は異なるハンドラに対して異なる名前の関数をスクリプトから探し出すので、これで構わな いのです。 次に、Basic HTTP 認証を使い、有効なユーザだけが許可されるよう Apache に指示します (これはかなり 基本的な Apache の設定なので、ここでは詳しく説明しません)。設定ファイルは以下のようになりました: <Directory /mywebdir> AddHandler mod_python .py PythonHandler myscript PythonAuthenHandler myscript PythonDebug On AuthType Basic AuthName "Restricted Area" require valid-user </Directory> さて、今度は認証のハンドラ関数を ‘myscript.py’ 内に記述しましょう。認証ハンドラは以下のようにな ります: from mod_python import apache def authenhandler(req): pw = req.get_basic_auth_pw() user = req.user if user == "spam" and pw == "eggs": return apache.OK else: return apache.HTTP_UNAUTHORIZED 一行づつ見てゆきましょう: • def authenhandler(req): ハ ン ド ラ 関 数 の 宣 言 で す。上 で も 説 明 し た よ う に 、mod_python は ディレ ク ティブ の 名 前 (PythonAuthenHandler) から先頭の ‘Python’ を落して小文字にした文字列から関数名をとる ので、関数名は authenhandler になります。 • pw = req.get_basic_auth_pw() 12 上記のようにしてパスワードを取得します。Basic HTTP 認証はパスワードを base64 でエンコードさ れたフォームで送信することでほんの僅かだけ分かりにくくして転送します。この関数はパスワード をデコードして、文字列にして返します。この関数はユーザ名を取得する前に呼び出さねばならない ので注意してください。 • user = req.user これでユーザが入力したユーザ名を取得します。 • if user == "spam" and pw == "eggs": return apache.OK ユーザが入力した値を比較し、期待通りの値なら処理を先に進めて apache.OK を返すよう Apache に指示します。Apache はこのリクエストのフェイズが完了したものとみなし、次のフェイズに進み ます (.py ファイルに対するリクエストであれば、次は handler() を呼び出します) • else: return apache.HTTP_UNAUTHORIZED 期待通りの値でなければ、Apache は HTTP_UNAUTHORIZED をクライアントに返します。通常、こ の戻り値を受け取ると、ブラウザはユーザ名とパスワードの入力を促すダイアログボックスを表示し ます。 13 四章 Python API 4.1 複数のインタプリタ mod_python を使っている場合,普段 Python でスクリプトを書いたりコマンドライン上で実効する際に は使わない,ある Python の機能が使われていることを知っておく必要があります.この機能は Python を 使っては呼び出せず,C 言語 API を介してのみアクセスできます. Python C API を使うと サブインタプリタ (subinterpreter) を生成できます.サブインタプリタの詳細は Py_NewInterpreter() 関数のドキュメントに書かれています.この議論の中では,各々のサブインタ プリタは独立した名前空間を持っていて,他のサブインタプリタからアクセスできないということだけ知っ ておいてもらえば十分です.この機能は,同じ Apache サーバの下で実行される別個のプログラムが影響を 及ぼし合わないようにする上でとても役立ちます. サーバの開始時や mod_python の初期化時には, mod_python は メイン (main) インタプリタと呼 ばれるインタプリタを起動メインインタプリタにはサブインタプリタの辞書が入ります.初期化時にはこ の辞書は空です.サーバはリクエストを受信するごとに,必要に応じてサブインタプリタを生成し,サブ インタプリタへの参照を辞書に記憶します.辞書は文字列をキーにしており,このキーは インタプリタ名 (interpreter name) といいます.インタプリタ名は任意の文字列にできます.メインインタプリタの名前は ‘main_interpreter’ です.その他のインタプリタを名付ける方法は PythonInterp* ディレクティブ で制御できます.デフォルトの動作では Apache の仮想サーバ名 (ServerName ディレクティブ) を使って名 前をつけます。すなわち、同じ仮想サーバのスクリプトは同じサブインタプリタで実行され、異なる仮想サー バで実行されるスクリプトは互いに完全に別個の名前空間を持つのです。PythonInterpPerDirectory および PythonInterpPerDirective ディレクティブを使うと、名前づけ規則を変更して、それぞれア クセス中のディレクトリの絶対パスを使ったり、Python*Handler のあるディレクトリの名前を使った りさせられます。PythonInterpreter を使うと、名前づけ規則による設定を特定の文字列で上書きで きます。 一度作成すると、サブインタプリタはそれ以降のリクエストで再利用されます。サブインタプリタは Apache のプロセスが死ぬまで破壊されることも終了することもありません。 現在どんな名前のインタプリタ下で実行中かは、req.interpreter を見れば分かります。 参考資料: Python C 言語 API (http://www.python.org/doc/current/api/api.html) Python C 言語 API 4.2 リクエストハンドラの概要 ハンドラ (handler) とは、リクエストにおける特定のフェイズを処理する関数です。 Apache は一つのリク エストをリクエストの読み込み、ヘッダの処理、コンテンツの提供、などといった複数のフェイズに分け 15 て処理します。いずれのフェイズに対しても、Apache のコア機能や、mod_python のようにユーザが Python で書いた関数に処理を渡すモジュール群の中からハンドラを呼び出します。 Python で書いたハンド ラは C で書いたハンドラと全く同じで、以下のような規則に従います: ハンドラ関数は常にリクエストオブジェクトへの参照を受け取ります。(このマニュアル全体を通じて、 リクエストオブジェクトはしばしば req という変数で参照されています。) 各ハンドラは以下のような値を返します: • apache.OK。現在のハンドラがあるリクエストのフェイズを処理し、エラーを起こさなかったこと を示します。 • apache.DECLINED。現在のハンドラがあるリクエストのフェイズを最後まで処理せず、Apache が 後続のモジュール内から適切なハンドラを探さねばならないことを示します。 • apache.HTTP_ERROR。 HTTP エラーが生じたことを示します。HTTP_ERROR は以下のいずれか になります: 16 HTTP_CONTINUE = HTTP_SWITCHING_PROTOCOLS = HTTP_PROCESSING = HTTP_OK = HTTP_CREATED = HTTP_ACCEPTED = HTTP_NON_AUTHORITATIVE = HTTP_NO_CONTENT = HTTP_RESET_CONTENT = HTTP_PARTIAL_CONTENT = HTTP_MULTI_STATUS = HTTP_MULTIPLE_CHOICES = HTTP_MOVED_PERMANENTLY = HTTP_MOVED_TEMPORARILY = HTTP_SEE_OTHER = HTTP_NOT_MODIFIED = HTTP_USE_PROXY = HTTP_TEMPORARY_REDIRECT = HTTP_BAD_REQUEST = HTTP_UNAUTHORIZED = HTTP_PAYMENT_REQUIRED = HTTP_FORBIDDEN = HTTP_NOT_FOUND = HTTP_METHOD_NOT_ALLOWED = HTTP_NOT_ACCEPTABLE = HTTP_PROXY_AUTHENTICATION_REQUIRED= HTTP_REQUEST_TIME_OUT = HTTP_CONFLICT = HTTP_GONE = HTTP_LENGTH_REQUIRED = HTTP_PRECONDITION_FAILED = HTTP_REQUEST_ENTITY_TOO_LARGE = HTTP_REQUEST_URI_TOO_LARGE = HTTP_UNSUPPORTED_MEDIA_TYPE = HTTP_RANGE_NOT_SATISFIABLE = HTTP_EXPECTATION_FAILED = HTTP_UNPROCESSABLE_ENTITY = HTTP_LOCKED = HTTP_FAILED_DEPENDENCY = HTTP_INTERNAL_SERVER_ERROR = HTTP_NOT_IMPLEMENTED = HTTP_BAD_GATEWAY = HTTP_SERVICE_UNAVAILABLE = HTTP_GATEWAY_TIME_OUT = HTTP_VERSION_NOT_SUPPORTED = HTTP_VARIANT_ALSO_VARIES = HTTP_INSUFFICIENT_STORAGE = HTTP_NOT_EXTENDED = 100 101 102 200 201 202 203 204 205 206 207 300 301 302 303 304 305 307 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 422 423 424 500 501 502 503 504 505 506 507 510 ハ ン ド ラ は HTTP エ ラ ー コ ー ド を 返 す 代 わ り に 、HTTP エ ラ ー コ ー ド を 例 外 の 値 に し て apache.SERVER_RETURN 例外を送出してエラーをシグナルできます。例えば、 raise apache.SERVER_RETURN, apache.HTTP_FORBIDDEN のようにします。 ハンドラは req.write() メソッドを使ってクライアントにコンテンツを送信できます。 POST リクエスト時のようにいクライアントデータがある場合、データは req.read() 関数を使って読 17 み出せます。 注意: Apache の Python*Handler ディレクティブを有効にしているディレクトリは sys.path のパ ス中に前置されます。サーバ設定ファイルの<Directory> 外でディレクティブを設定した場合、ディレ クトリの位置は分からず、sys.path にも加えられません。 最小限のハンドラの例は以下のようになります: from mod_python import apache def requesthandler(req): req.content_type = "text/plain" req.write("Hello World!") return apache.OK 4.3 フィルタハンドラの概要 フィルタハンドラ (filter handler) とは、サーバへの入力やサーバからの出力をフィルタする関数です。 フィ ルタには二つの種類、入力 (input) および出力 (output) があり、それぞれサーバへの入力とサーバからの出 力に適用されます。 現時点では、mod_python はリクエストレベルのフィルタしかサポートしていません。つまり、フィル タできるのは HTTP リクエストか、レスポンスの本体だけです。 Apache は接続レベルのフィルタをサポー トしており、mod_python ではこの機能を将来サポートする予定です。 フィルタハンドラは フィルタ (filter) オブジェクトを引数にとります。リクエストオブジェクトは filter.req から利用できますが、全ての読み書きはフィルタオブジェクトの read および write メ ソッドで行わねばなりません。 フィルタは read 操作が None を返した (ストリームの終端、EOF に到達したことを示します) 時点で閉 じねばなりません。 フィル タ の 戻 り 値 は 無 視 さ れ ま す。フィル タ は ハ ン ド ラ と 違って 処 理 を 拒 否 で き ま せ ん が 、 filter.pass_on() メソッドを使えば同様の効果を引き起こせます。 フィルタはまず PythonIInputFilter または PythonOutputFilter ディレクティブを使って登録 し、その後で、Apache の Add/SetInputFilter または Add/SetOutputFilter ディレクティブで登 録せねばなりません。 出力フィルタの指定方法を以下に示します。この例では、 .py ファイルは全て CAPITALIZE フィルタで 処理するようサーバに指示します: PythonOutputFilter capitalize CAPITALIZE AddOutputFilter CAPITALIZE .py そして、‘capitalize.py’ のコードは以下のようになります: 18 from mod_python import apache def outputfilter(filter): s = filter.read() while s: filter.write(s.upper()) s = filter.read() if s is None: filter.close() フィルタを書くときには、フィルタの上流にあるものが IO 操作を行うたびにフィルタが呼び出されるこ と、そしてフィルタは流し込まれるデータの量をコントロールできず、リクエスト処理のどこでフィルタ が呼び出されるかを知る術もないということを心に留めておきましょう。 例えば、単一のリクエストの中でフィルタが呼び出される回数は一回だけかもしれないし、五回かもし れません。また、リクエストの完了や、あるリクエストに対してどれが最初あるいは最後のフィルタ呼び 出しであるかを前もって知る術もありません。ただ EOS (read 操作が None を返す) だけが、リクエスト の末端を強く示唆しているだけにすぎません。 また、サブリクエストを行うと、フィルタが再帰的に呼び出されっぱなしになりうることにも注意して おきましょう。データを複数回フィルタさせないためには、req.main の値を調べ、常にサブリクエスト の処理ではないことを確かめるようにしてください。 フィルタの詳細については http://httpd.apache.org/docs-2.0/developer/filters.html を参照してください。 4.4 接続ハンドラの概要 接続ハンドラ (connection handler) は接続に関わる処理を行います。接続ハンドラはクライアントからサー バへの TCP 接続が作成された直後に開始します。 HTTP ハンドラと違い、接続ハンドラは接続 (connection) オブジェクトを引数にとります。 接続ハンドラはプロトコルの実装に使います。例えば以下は簡単な echo サーバの例です: Apache の設定: PythonConnectionHandler echo echo.py ファイルの内容: from mod_python import apache def connectionhandler(conn): while 1: conn.write(conn.readline()) return apache.OK 19 4.5 apache – Apache 内部へのアクセス Apache 内部への Python インタフェースは、mod_python パッケージ内の apache という分かりやすい 名前のモジュールに入っています。このモジュールでは Apache の内部構造体に対応づけされた重要なオ ブジェクトや便利な関数群を提供しています。これらはすべて後に説明します。(リクエストオブジェクト でも Apache 内部へのインタフェースを提供していますが、これについてはマニュアル中の別の節で触れ ます。) The apache モジュールを import できるのは mod_python 下で動作するスクリプトだけです。これは、 apache が mod_python の提供している_apache に依存しているからです。 モジュールの import は以下のように行うのがベストです: from mod_python import apache mod_python.apache モジュールでは、以下の関数とオブジェクトを定義しています。Apache の内部 に関する詳しい説明は、Apache Developer page を参照してください。 4.5.1 関数 log_error(message[, level, server ]) Apache の ap_log_error() 関数へのインタフェースです。message はエラーメッセージの入った 文字列です。level は以下のフラグ定数のいずれかです: APLOG_EMERG APLOG_ALERT APLOG_CRIT APLOG_ERR APLOG_WARNING APLOG_NOTICE APLOG_INFO APLOG_DEBUG APLOG_NOERRNO server は req.server オブジェクトへの参照です。server を指定しない場合、エラーがデフォル トのエラーログまたは適切な仮想サーバのエラーログに記録されます。server を指定しない場合、 ‘LogLevel’ ディレクティブの設定は適用されず、‘LogLevel’ の設定は httpd をコンパイルした時 のデフォルト値になります。通常、デフォルト値は warn です。 リクエストオブジェクトを参照できるような状況では、req.log_error を使うよう検討してくだ さい。req.log_error はリクエスト元の IP アドレスのようなリクエスト固有の情報をログエント リに追加します。 import_module(module_name[, autoreload=1, log=0, path=None ]) この関数を使うと、import しておいたモジュールが変更された時に自動的に再ロードを行うという、 mod_python の内部メカニズムの恩恵を受けられます。 module_name はモジュール名の入った文字列です。(mypackage.mymodule のように、ドットを含 めてもかまいません。) autoreload には import 後にモジュールが更新された場合に再ロードを行うべ きか否かを指定します。log を真にすると、モジュールを再ロードした時にメッセージをログ書き出 します。path を指定すると、特定のパスからモジュールを import するよう制限できます。 一例を示します: 20 from mod_python import apache mymodule = apache.import_module(’mymodule’, log=1) allow_methods([*args ]) req.allowed の値を設定するための便宜関数です。req.allowed はビットマスクで、‘Allow:’ ヘッダの作成に使われます。この値は HTTP_NOT_IMPLEMENTED エラーを返すよりも前に設定して おかねばなりません。 引数には以下の値から一つまたは複数個を指定します: M_GET M_PUT M_POST M_DELETE M_CONNECT M_OPTIONS M_TRACE M_PATCH M_PROPFIND M_PROPPATCH M_MKCOL M_COPY M_MOVE M_LOCK M_UNLOCK M_VERSION_CONTROL M_CHECKOUT M_UNCHECKOUT M_CHECKIN M_UPDATE M_LABEL M_REPORT M_MKWORKSPACE M_MKACTIVITY M_BASELINE_CONTROL M_MERGE M_INVALID config_tree() サーバレベルの設定ツリーを返します。このツリーには.htaccess 由来のディレクティブは入ってい ません。また、ツリーはコピー にすぎず、変更しても実際の設定になんら影響を及ぼしません。 server_root() ServerRoot の値を返します。 make_table() この関数は撤廃され、table (下記参照) の別名になっています。 mpm_query(code) Allows querying of the MPM for various parameters such as numbers of processes and threads. この関数 を使うと、プロセス数やスレッド数といった様々な値を MPM に問い合わせられます。戻り値は以下 の三つの定数のいずれかになります: 21 AP_MPMQ_NOT_SUPPORTED = 0 AP_MPMQ_STATIC = 1 AP_MPMQ_DYNAMIC = 2 # # # # # # MPM がスレッド生成や fork を サポートしていないことを示します。 MPM がスレッドやデーモン (daemon) の数を 静的に設定していることを示します。 MPM がスレッドやデーモンの数を 動的に設定していることを示します。 code 引数は以下の値のいずれかでなければなりません: AP_MPMQ_MAX_DAEMON_USED = AP_MPMQ_IS_THREADED = AP_MPMQ_IS_FORKED = AP_MPMQ_HARD_LIMIT_DAEMONS = AP_MPMQ_HARD_LIMIT_THREADS = AP_MPMQ_MAX_THREADS = AP_MPMQ_MIN_SPARE_DAEMONS = AP_MPMQ_MIN_SPARE_THREADS = AP_MPMQ_MAX_SPARE_DAEMONS = AP_MPMQ_MAX_SPARE_THREADS = AP_MPMQ_MAX_REQUESTS_DAEMON= AP_MPMQ_MAX_DAEMONS = 1 2 3 4 5 6 7 8 9 10 11 12 # # # # # # # # # # # # これまでに使われたデーモン数の最大値 MPM がスレッドを生成できるか否か MPM が fork を実行できるか否か 最大デーモン数のコンパイル時の設定 最大スレッド数のコンパイル時の設定 設定上のスレッド/子プロセス数 スペアとして残すデーモン数の最小値 スペアとして残すスレッド数の最小値 スペアとして残すデーモン数の最大値 スペアとして残すスレッド数の最大値 デーモンあたりの最大リクエスト数 設定上でのデーモンの最大数 以下に例を示します: if apache.mpm_query(apache.AP_MPMQ_IS_THREADED): # do something else: # do something else 4.5.2 テーブルオブジェクト (mp_table) class table([mapping-or-sequence ]) mp_table 型の新たな空のオブジェクトを返します。テーブルオブジェクトの説明は 4.5.2 節を参照 してください。mapping-or-sequence はテーブルの初期値設定に使われます。 テーブルオブジェクトは Apache APR テーブルに対するラッパです。このテーブルオブジェクトは、 (Python 2.2 からサポートされた in 演算子なども含めて) ほぼ辞書と同じようにふるまいます。ただ し、以下の点で異なります: •キーと値はいずれも文字列でなければなりません。 •キーの参照時に大文字と小文字を区別しません。 •キーの重複があってもかまいません (add() メソッドを参照してください) 一つのキーに対して 複数の値が存在する場合、添字参照 (subscript operation) を行うとリストを返します。 このテーブルには、Apache の使う多くの情報、例えば req.headers_in や req.headers_out などが入っています。 mod_python がリクエストオブジェクトの中で提供しているテーブルは、全て Apache の構造体に 対する実マッピングなので、 Python 側でテーブルを変更すると、根底にある Apache のテーブルも 変更されます。 通常の辞書に似たふるまいの他に、テーブルオブジェクトには以下のメソッドがあります: 22 add(key, val) 重複するキーを作成できるようにします。Set-Cookie: のような、同じキーを持つ複数のヘッ ダが必要な場合に便利です。 3.0 で追加された仕様です。 4.5.3 リクエストオブジェクト リクエスト (request) オブジェクトは、Apache の request_rec 構造体に対応する Python のオブジェクト です。ハンドラが起動される時には、常にリクエストオブジェクトが単一の引数として渡されます。 リクエストオブジェクトには動的に属性値を代入でき、ハンドラ間のやりとりに利用できます。 メソッド add_common_vars() Apache の ap_add_common_vars() 関 数 を 呼 び 出 し ま す。こ の メ ソッド を 呼 び 出 す と 、 req.subprocess_env には CGI に関する多くの情報が入ります。 add_handler(htype, handler[, dir ]) この関数を使うと動的にハンドラを追加できます。htype は、たとえば ‘PythonHandler’ のよう に、任意の (ただしフィルタハンドラや接続ハンドラは除きます) Apache リクエストハンドラのディ レクティブ名が入った文字列です。handler はモジュールやハンドラ関数の名前が入った文字列です。 dir はオプションで、sys.path に追加するディレクトリ名の入った文字列です。ディレクトリ名 を指定しない場合、もしすでに同じタイプのハンドラが登録されていれば、その設定を継承します。 PythonPath ディレクティブが有効であるばあい、sys.path は PythonPath の設定だけに従い ます (ディレクトリの追加は行わず、dir は無視されます)。 この関数を使って追加したハンドラは、リクエストの終了までしか存続しません。あるハンドラの内 部で同じタイプのハンドラをさらに追加してもかまいませんが、その場合は無限ループを形成しない ように気を付けねばなりません。 動的なハンドラ登録はコードの次の振舞いを動的に変更できる便利なテクニックです。典型的な例 は、認証のレベルに応じて異なる PythonHandler を切替えるような PythonAuthenHandler で しょう: if manager: req.add_handler("PythonHandler", "menu::admin") else: req.add_handler("PythonHandler", "menu::basic") 注意: この関数は、正しいハンドラ名を使っているかどうかをチェックしません。不正なハンドラ名 を渡した場合には無視されます。 allow_methods(methods[, reset ]) methods を req.allowed_methods リ ス ト に 追 加 し ま す。こ の リ ス ト は HTTP_METHOD_NOT_ALLOWED や HTTP_NOT_IMPLEMENTED が ク ラ イ ア ン ト に 返 さ れ る 時 に Allowed: ヘッダに渡されます。Apache は何らメソッド制限に対する操作を行わず。リストはた だ単にヘッダの構築に使われるだけです。実際にメソッドを制限するロジックはハンドラコードで書 かねばなりません。 methods は文字列からなる配列です。reset が 1 の場合、最初にメソッドのリストを消去します。 23 document_root() DocumentRoot の設定を返します。 get_basic_auth_pw() Basic 認証で使われるパスワードの入った文字列を返します。 get_config() 現在有効になっている mod_python の設定から Python*Handler および PythonOption の 設定を除いた内容の入ったテーブルオブジェクトへの参照を返します (PythonOption の設定は req.get_options() で取得できます)。テーブルのキーはディレクティブ名になり、テーブルの 値はディレクティブの値 (あれば) になります。 get_remote_host([type, str_is_ip ]) 遠隔のクライアントの DNS 名および IP アドレスを決定するためのメソッドです。この関数は最初の 呼び出し時に DNS への参照を伴うことがありますが、それ以降の呼び出しでは結果をキャッシュし て使います。 type 引数は以下のような値に指定できます: •apache.REMOTE_HOST DNS を参照します。Apache の HostNameLookups が off の場合 や、ホスト名を特定できなかった場合には None を返します。 •apache.REMOTE_NAME (デフォルト) DNS 名があれば返し、なければ IP アドレスを (10 進ドッ ト表記の文字列で) 返します。 •apache.REMOTE_NOLOOKUP DNS 名の参照を行わず、IP アドレスを返します。注意: 以前の 呼び出しで DNS を参照した場合、その結果としてキャッシュされたホスト名を返します。 •apache.REMOTE_DOUBLE_REV 二重逆引きを強制的に行います。失敗すると None を返し ます。 str_is_ip を None にしたり省略したりすると、戻り値は DNS 名または IP アドレスのいずれかを表現 した文字列になります。 str_is_ip 引数を None 以外の値にすると、戻り値は (address, str_is_ip) からなるタプルにな ります。このとき、address が IP アドレスを示す文字列であれば、タプルの str_is_ip はゼロ以外 の値になります。それ以外の場合には None を返します。 get_options() PythonOption ディレクティブで設定したオプションの入ったテーブルオブジェクトへの参照を返 します。 internal_redirect(new_uri) リクエストを new_uri に内部的にリダイレクトします。new_uri は文字列でなければなりません。 httpd サーバは、新たなリクエストオブジェクトと全てのリクエストフェイズを生成して内部的なリ ダイレクションを実現します。内部リダイレクションの中では、req.prev にリダイレクト元のリ クエストオブジェクトへの参照が入ります。 log_error(message[, level ]) Apache の ap_log_rerror 関数へのインタフェースです。message はエラーメッセージの入った文 字列で、level は以下のフラグ定数のいずれかでなければなりません: 24 APLOG_EMERG APLOG_ALERT APLOG_CRIT APLOG_ERR APLOG_WARNING APLOG_NOTICE APLOG_INFO APLOG_DEBUG APLOG_NOERRNO リクエストオブジェクトへの参照がない状態でログを記録したければ、apache.log_error 関数 を使ってください。 requires() require ディレクティブの引数に指定した文字列からなるタプルを返します。 例えば、Apache の設定が以下のようになっているとすると: AuthType Basic require user joe require valid-user requires() は (’user joe’, ’valid-user’) を返します。 read([len ]) クライアントから最大 len バイトのデータを直接読み出し、読み出したデータを文字列にして返しま す。len 引数を省略するか、負の値に指定すると、クライアントから提供される全てのデータを読み 出します。 この関数は Apache の Timeput 設定ディレクティブの影響を受けます。クライアントからデータを 読み出す際に Timeout に指定した時刻を超過すると、読み出し操作を中断して IOError を送出し ます。 こ の 関 数 で は ク ラ イ ア ン ト が Content-length ヘッダ を 提 供 す る も の と 想 定 し て い ま す。 Content-length ヘッダがなければ、Content-length: 0 であるかのように扱います。 Content-length が誤った値である場合、この関数はクライアントが提供できる以上のデータを読 みだそうとして、その結果 Timeout に到達するまで処理をブロックさせる可能性があります。 readline([len ]) read() に似ていますが、行末までデータを読み込みます。 注意: HTTP の仕様により、ほとんどのクライアントは行末を ‘\n’ ではなく ‘\r\n’ で終端します。 readlines([sizehint ]) readline を使って sizehint バイトまでの全てのデータを読み出し、その結果をリストで返します。 register_cleanup(callable[, data ]) 後処理 (cleanup) 用の関数を登録します。 callable は任意の呼び出し可能オブジェクトにできます。オ プションの引数 data は任意のオブジェクトにできます (デフォルトでは None です)。callable は、リ クエストが終了した直後でかつ Apache が実際のリクエストレコードを破壊する直前に、data を引数 にして呼び出されます。 リクエストオブジェクトを data に渡しても問題はありませんが、後処理を実行している時にはリク エスト処理はほとんど完結しており、クライアントへの書き込みのような操作には全く意味がないと いうことを心に留めておいてください。 25 後処理中にエラーが生じた場合、そのことを必ずエラーログに記録せねばなりません。後処理におけ るエラーはリクエストの処理に何ら影響を及ぼさないため、後処理におけるバグを追求しにくくなる からです。 後処理の実行に入る前にサーバがシャットダウンを始めると、後処理が実行されない可能性があり ます。 sendfile(path[, offset, len ]) ファイル path のオフセット offset 以降の len バイトをサーバの内部 API を使ってクライアントに送信 します。offset のデフォルトは 0 で、 len のデフォルトは -1 (ファイル全体を送信する) です。 この関数はクライアントにファイルを送信する最も効率的な方法です。 write(string[, flush=1 ]) string をクライアントに直接書き出します。flush が 0 でない限り、書き出し後にバッファをフラッシュ します。 flush() 出力バッファをフラッシュします。 set_content_length(len) req.clength および ‘Content-Length’ ヘッダの値を len に sっ定します。ヘッダが送信されて しまった後 (req.write() などを呼び出してデータ本体の最初のバイトが書き出してしまった後) では、この関数を呼び出しても無意味なので注意してください。 メンバ connection リクエストに関連づけられた接続オブジェクトです。詳しくは接続オブジェクトのの説明を参照して ください。(読み出し専用) server リクエストに関連づけられたサーバオブジェクトです。詳しくはサーバオブジェクトのの説明を参照 してください。(読み出し専用) next リクエストが内部リダイレクトの場合、リダイレクト先のリクエストオブジェクトを指します。(読 み出し専用) prev リクエストが内部リダイレクトの場合、リダイレクト元のリクエストオブジェクトを指します。(読 み出し専用) main リクエストがサブリクエストの場合、メインリクエストを指します。(読み出し専用) the_request リクエストの最初の行の内容が入った文字列です。(読み出し専用) assbackwards リクエストが HTTP/0.9 の “simple” リクエストである、すなわち、レスポンスにはヘッダがなく、ボ ディだけになることを示します。このメンバは古いブラウザ向けに互換性を保つためだけに存在して いますが、assbackwards を 1 に設定すると、内部リダイレクトから返ってきたレスポンスの一部を取 り出したいときにヘッダの送信を抑制できるので便利だと指摘する人もいます。 proxyreq プロキシリクエストです: 値は apache.PROXYREQ_* の一つになります。(読み出し専用) 26 header_only リクエストが HEAD リクエストであることを示すブール値です。逆は GET です。(読み出し専用) protocol クライアントが指定してきたプロトコルか、‘HTTP/0.9’ になります。CGI の SERVER_PROTOCOL と同じです。(読み出し専用) proto_num プロトコルのバージョンを表す整数値です; 1.1 は 1001 になります。(読み出し専用) hostname 完全 URI または ‘Host:’ ヘッダで指定されたホスト名を表す文字列です。(読み出し専用) request_time リクエストの開始時刻を表す長整数です。(読み出し専用) status_line ‘200 OK’ のようなステータスコード行です。(読み出し専用) status ステータスコードです。apache.HTTP_* のいずれかの値になります。 method ’GET’, ’HEAD’, ’POST’ などの、リクエストメソッドを表す文字列です。CGI の REQUEST_METHOD と同じです。(読み出し専用) method_number リクエストメソッドの番号を表す整数です。(読み出し専用) allowed 利 用 可 能 な メ ソッド を 表 す 整 数 ビット フィー ル ド で す。HTTP_METHOD_NOT_ALLOWED や HTTP_NOT_IMPLEMENTED で応答する際の ‘Allowed:’ ヘッダの構築に使われます。このフィー ルドは Apache が内部で使います。 ‘Allowed:’ を設定したければ、4.5.3 節で述べたように req.allow_methods() メソッドを使ってください。(読み出し専用) allowed_xmethods 利用可能な拡張メソッドからなるタプルです。(読み出し専用) allowed_methods 利用可能なメソッドからなるタプルです。 METHOD_NOT_ALLOWED と一緒に使われます。4.5.3節で 述べたように、このメンバの内容は req.allow_methods() で変更します。(読み出し専用) sent_bodyct ストリーム中におけるデータボディのバイト長を表す整数です。(読み出し専用) bytes_sent 送信されたバイト数を表す長整数です。(読み出し専用) mtime リソースが更新された時刻を表す長整数です。(読み出し専用) chunked チャンク分割転送方式 (chunked transfer-coding) のデータを送信していることを示すブール値です。 (読み出し専用) range Range: ヘッダの内容が入った文字列です。(読み出し専用) clength 「真の」コンテンツ長が入った長整数です。(読み出し専用) 27 remaining まだ読み出していないバイト長を表す長整数です (読み出し操作のときにしか意味をなしません)。(読 み出し専用) read_length 読み出されたバイト長を表す長整数です。(読み出し専用) read_body リクエストボディの読み出し方法を表す整数です。(読み出し専用) read_chunked チャンク分割転送方式で読み出していることを示すブール値です。(読み出し専用) expecting_100 クライアントが 100 (HTTP_CONTINUE) レスポンスを待っていることを示すブール値です。(読み出 し専用) headers_in クライアントから送信されたヘッダの入ったテーブルオブジェクトです。 headers_out クライアントに送信するヘッダを表す table オブジェクトです。 err_headers_out エラーレスポンスの際に ‘headers_out’ の代わりに送信されるヘッダです。 subprocess_env CGI でよく使われる環境情報の入った table オブジェクトです。このテーブルに必要な情報を入れ るには、まず req.add_common_vars() を呼び出さねばならないかもしれません。 notes 雑多で汎用のデータをリクエストが存続している間記憶しておくための table オブジェクトです。 データをハンドラ間で渡す必要がある場合には、notes を使うよりも単にリクエストオブジェクト に新たなメンバを追加した方がよいでしょう。 phase 例えば ‘PythonHandler’ のような、現在処理中のフェイズを表す文字列です。(読み出し専用) interpreter ハンドラを実行しているサブインタプリタの名前です。(読み出し専用) content_type コンテンツのタイプ (content type) を表す文字列です。mod_python は、Python プログラム中で content_type を手動で設定したかどうかを内部フラグ (req._content_type_set) を使って 追跡します。publisher ハンドラは、content_type を明示的に設定されていない場合、出力の最初 の数バイトを調べてコンテンツのタイプを推測しようと試みます。 handler 現在処理中のハンドラの名前です。この値は mod_python ハンドラが設定した値ではなく、mod_mime が設定したハンドラ名になります。多くの場合、値は ‘"mod_python"’ になります。(読み出し専用) content_encoding コンテンツのコード方式 (content encoding) を表す文字列です。(読み出し専用) vlist_validator (コンテントネゴシエーション中の場合に使う) 異体リスト検証子 (variant list validator) を表す文字列 です。(読み出し専用) user 認証チェックが行われた場合にユーザ名の入るメンバです。CGI における REMOTE_USER と同じで 28 す。(読み出し専用) 注意: この値を読み出す場合、前もって req.get_basic_auth_pw() を呼び出しておかねばなり ません。 ap_auth_type 認証の形式です。CGI における AUTH_TYPE と同じです。(読み出し専用) no_cache レスポンスをキャッシュできないことを示すブール値です。(読み出し専用) no_local_copy レスポンスのローカルコピーが存在しないことを示すブール値です。(読み出し専用) unparsed_uri 解析処理前の URI です。(読み出し専用) uri URI のパス部分です。(読み出し専用) filename 要求されているファイル名です。 canonical_filename 真のファイル名を表す文字列です (req.filename と一致しない場合、req.filename は正規化 されます) (読み出し専用) path_info ファイル名より後で、かつクエリ引数より前の位置に何かある場合、その内容を表す文字列になりま す。CGI における PATH_INFO です。(読み出し専用) args CGI における QUERY_ARGS を表す文字列です。(読み出し専用) finfo URI の指し示すファイルについて説明している、POSIX の stat に似たファイル情報構造体を表すタプル です。(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime, fname, name) からなります。apache モジュールでは、例えば: fname = req.finfo[apache.FINFO_FNAME] のように、構造体の各要素にアクセスするための定数 FINFO_* を定義しています。(読み出し専用) parsed_uri URI を各要素に分解したタプルです。(scheme, hostinfo, user, password, hostname, port, path, query, fragment) からなります。apache モジュールでは、例えば: fname = req.parsed_uri[apache.URI_PATH] のように、構造体の各要素にアクセスするための定数 URI_* を定義しています。(読み出し専用) used_path_info 現在のリクエストに対して ‘path_info’ 受け入れるか拒否するかを決めるフラグです。(読み出し 専用) eos_sent EOS bucket (ストリームの終了を表すデータ) を送信済みであることを表すブール値です。(読み出し 専用) 29 4.5.4 接続オブジェクト (mp_conn) 接続 (connection) オブジェクトは、Apache の conn_rec 構造体に対応する Python のオブジェクトです。 メソッド read([length ]) 最大で length バイトをクライアントから読み出します。この関数を呼び出すと、少なくとも 1 バイト 読み出すまで無期限でブロックします。length を -1 にすると、クライアントがソケットを閉じられ るまでデータを読み続けようとします (http サーバのコードで EXHAUSTIVE モードとして知られる 動作です)。 このメソッドを使うのは 接続ハンドラ (Connection Handler) の中だけにしてください。 注意: このメソッドの挙動はバージョン 3.0.3 以降で変更されました。 3.0.3 とそれ以前のバージョン では length バイト読み出すまでブロックしていました。 readline([length ]) 接続オブジェクトから一行のデータか、あるいは length バイトまでのデータを読み出します。 このメソッドを使うのは 接続ハンドラ の中だけにしてください。 write(string) string をクライアントに送信します。 このメソッドを使うのは 接続ハンドラ の中だけにしてください。 メンバ base_server 接続を受け入れた物理ホストの server オブジェクトです。(読み出し専用) local_addr サーバの情報を表す ‘(address, port)’ からなるタプルです。(読み出し専用) remote_addr ‘ク’ ライアントの情報を表す ‘(address, port)’ からなるタプルです。(読み出し専用) remote_ip クライアントの IP アドレスを表す文字列です。CGI の REMOTE_ADDR と同じです。(読み出し専用) remote_host リモートのクライアントの DNS 名を表す文字列です。DNS を使ったチェックをまだ行っていない場 合、名前が見つからなければこの値は "" (空文字列) になります。CGI の REMOTE_HOST と同じで す。(読み出し専用) remote_logname RFC1413 (ident) を使って解決したリモート名です。CGI の REMOTE_IDENT と同じです。(読み出し 専用) aborted 接続が中断されたことを示すブール値です。(読み出し専用) keepalive 整数型のフラグで、 接続を次のリクエストまで維持する場合には 1、 「不確定 (undecided) の場合には 0、「致命的なエラー (fatal error)」の場合には -1 になります。(読み出し専用) double_reverse 整数型のフラグで、DNS の二重逆引きをすでに行っている場合には 1, まだならば 0、二重逆引きを 30 行って失敗した場合には -1 になります。(読み出し専用) keepalives 接続の使われた回数です。(読み出し専用) local_ip サーバの IP を表す文字列です。(読み出し専用) local_host サーバの DND 名です。(読み出し専用) id 一意な接続 id を表す長整数型の値です。(読み出し専用) notes 雑多な汎用の情報を入れる table オブジェクトです。接続が生きている間維持されます。 4.5.5 フィルタオブジェクト (mp_filter) フィルタオブジェクト (filter object) は mod_python で入力フィルタや出力フィルタとして渡すオブジェク トです。このオブジェクトを使ってフィルタの情報を得たり、フィルタスタック上の隣接するスタックと 情報をやり取りしたりします。 メソッド pass_on() 全てのデータに一切の処理を施さずフィルタを通過させます。 read([length ]) 隣のフィルタから最大で len バイトを読み出し、読み出したデータを文字列で返します。すでにスト リームの終端 (EOS) に到達していた場合には None を返します。EOS に到達した場合、フィルタは 閉じられなければなりません。 len 引数を負の値にしたり省略したりした場合、読み出せる全てのデータを読み出します。 readline([length ]) 隣のフィルタから一行のデータか、あるいは length バイトまでのデータを読み出します。 write(string) string を隣のフィルタに送信します。 flush() FLUSH bucket (ストリームのフラッシュを指示するデータ) を送信して出力バッファをフラッシュし ます。 close() フィルタを閉じて EOS bucket を送信します。フィルタを閉じた後、フィルタに対する IO 操作を行う と例外を送出します。 disable() このハンドラを無視して、データを素通しするよう mod_python に指示します。このメソッドは mod_python が内部的に使用していて、フィルタ内で例外が生じた際にトレースバック出力が無限 ループを引き起こさないようにしています。 31 メンバ closed フィルタが閉じられたことを示すブール値です。(読み出し専用) name フィルタの登録名を表す文字列です。(読み出し専用) req リクエストオブジェクトへの参照です。(読み出し専用) is_input ブール値で、フィルタが入力フィルタである場合に真になります。Boolean. True if this is an input filter. (読み出し専用) handler 設定ファイル中で指定した、このフィルタの Python ハンドラの名前を表す文字列です。(読み出し 専用) 4.5.6 サーバオブジェクト (mp_server) サーバオブジェクト (server object) は、Apache の server_rec 構造体に対応する Python のオブジェクト です。サーバ構造体は、リクエストを受け付けるサーバ (場合によっては仮想サーバ) を表現します。 メソッド get_config() req.get_config() に似ていますが、Apache の設定ベクタ server->module_config の指す 設定情報を返します。 register_cleanup(request, callable[, data ]) 後処理 (cleanup) 用の関数を登録します。 req.register_cleanup() に非常に良く似ていますが、 後処理は子プロセスの終了時に実行されます。この関数には必須の引数が一つあり、リクエストオブ ジェクトを渡します。 メンバ defn_name サーバ定義の書かれている設定ファイルの名前を表す文字列です。(読み出し専用) defn_line_number 設定ファイル中でサーバの設定が書かれている部分の行番号を表す整数です。(読み出し専用) server_admin ServerAdmin ディレクティブの値です。(読み出し専用) server_hostname ServerName ディレクティブの値です。CGI の SERVER_NAME と同じです。(読み出し専用) port TCP/IP ポート番号です。CGI の SERVER_PORT と同じです。このメンバは Apache 2.0 では 0 のよ うです。代わりに req.connection.local_addr を参照してください (読み出し専用) error_fname サーバにエラーログファイルがある場合、その名前を表す文字列です。(読み出し専用) 32 loglevel ログレベルを表す整数です。(読み出し専用) is_virtual ブール値で、バーチャルサーバのときに真になります。(読み出し専用) timeout 整数値で、Timeout ディレクティブの値です。(読み出し専用) keep_alive_timeout 整数値で、Keepalive のタイムアウトを表す値です。(読み出し専用) keep_alive_max 一つの Keepalive 接続が受け入れるリクエスト数の最大値です。(読み出し専用) keep_alive 永続的な接続を使っているかどうかを表す値です。(読み出し専用) path 文字列で、ServerPath に指定したパスです。(読み出し専用) pathlen 文字列で、パスの長さです。(読み出し専用) limit_req_line 整数値で、HTTP リクエスト行のサイズの上限です。Integer. Limit on size of the HTTP request line. (読み出し専用) limit_req_fieldsize 整数値で、リクエストヘッダのフィールド長の上限です。(読み出し専用) limit_req_fields 整数値で、リクエストヘッダのフィールド数の上限です。(読み出し専用) 4.6 util – 雑多なユーティリティ util モジュールでは、Web アプリケーションの開発者にとって便利な、標準ライブラリの cgi モジュー ルのような数々のユーティリティを提供しています。 util モジュールの実装は cgi よりも格段に高速です。というのも、CGI を使う場合には情報を受け渡 すのに環境変数を使うのに対し、util は Apache の API を直接呼び出しているからです。 このモジュールを使うには: from mod_python import util のようにして import するよう勧めます。 参考資料: Common Gateway Interface RFC Project Page (http://CGI-Spec.Golux.Com/) CGI に関する仕様の詳しい情報です。 4.6.1 FieldStorage クラス フォームデータへのアクセスは FieldStorage クラスを使って行います。このクラスは標準ライブラリ モジュール cgi の FieldStorage によく似ています。 33 class FieldStorage(req[, keep_blank_values, strict_parsing ]) クライアントから提出された HTML フォームデータに一様な方法でアクセスする手段を提供します。 req は mod_python のリクエストオブジェクトのインスタンスです。 オプションの引数 keep_blank_values は、URL エンコードされたフォームデータ中のブランクの値を 空文字列として扱うかどうかを指示するフラグです。デフォルトの値は False であり、ブランクの 値を無視して値がなかったかのようにふるまいます。 オプションの引数 strict_parsing はまだ実装されていません。 初期化の過程で、FieldStorage クラスはクライアントから得た全てのデータを読み出します。こ の時点でクライアントから得られたデータは全て消費されてしまうため、一つのリクエストに対して は一つの FieldStorage クラスしかインスタンス化できません。また、FieldStorage をインス タンス化する前にいかなるクライアントデータを読み出そうとしてもなりません。 次に、クライアントから読み出されたデータは解析され、個別のフィールドに分割され、各フィール ドごとに Field オブジェクトとしてパックされます。file 型の HTML フォーム入力の場合、一時 ファイルが作成され、Field オブジェクトの file 属性からアクセスできるようになります。 FieldStorage クラスはマップ型オブジェクトのインタフェースを備えています。つまり、このク ラスのインスタンスは辞書のように扱えるのです。マップ型として使う場合、キーはフォームの名前 で、戻り値は以下のいずれかになります: •StringField クラスのインスタンスで、フォームに入力された値が入っています。ある名前の フォームに対して値が一つだけ対応している場合に限り、この形式になります。StringField は str のサブクラスで、標準ライブラリの cgi モジュールと互換性をもたせるために value 属性が追加されています。 •ファイルのアップロード入力の場合の Field クラスのインスタンスです。 •StringField かつ/または Field オブジェクトからなるリストです。<select> フォームエ レメントのように、複数の値が存在する場合にはこの形式になります。 注意: 標準ライブラリの cgi モジュールにおける FieldStorage クラスと違い、Field オブジェ クトを返すのはファイルのアップロード時だけ です。それ以外の場合には StringField のインス タンスを返します。従って、ほとんどの場合、フィールドの値にアクセスするために .value 属性 を使う必要はありません。 標準のマップ型オブジェクトがサポートしているメソッドの他に、FieldStorage オブジェクトで は以下のような属性があります: list 各入力に対する Field オブジェクトでできたリストです。同じ名前に対して複数の入力があっ た場合、リスト中には複数の要素が入ります。 以下は FieldStorage のメソッドです: getfirst(name[, default ]) フォームフィールド name に関連づけられた値を常に一つの値にして返します。対応するフォー ムフィールドや値が存在しない場合には、オプションの引数 default に指定された値を返します。 default を指定しない場合、デフォルトの値は None です。 getlist(name) このメソッドは name に関連づけられた値を常にリストにして返します。name に対応するフォー ムフィールドや値が存在しない場合には空のリストを返します。値が一つだけの場合にも、要 素が一つのリストを返します。 34 4.6.2 Field クラス class Field() FieldStorage クラスが内部で使っているクラスで、ユーザがインスタンス化するためのものでは ありません。個々の Field クラスのインスタンスは HTML フォーム入力を表現します。 Field には以下の属性があります: name 入力名です。 value 入力値です。この属性はファイルアップロードに対するデータを読み出す際にも使えますが、 value を使ってファイルにアクセスする場合、ファイル全体を一度メモリに読み込むので、巨 大なファイルを扱う場合によく注意してください。 file ファイルオブジェクトです。ファイルアップロードの場合には一時ファイルを指しています。単 純な値の場合には StringIO オブジェクトになるので、value の代わりにこの値を使っても 単純な文字列値を読み出せます。 filename クライアントから提供されたファイルの名前です。 type クライアントから提供された、この入力のコンテンツタイプです。 type_options クライアントが content-type で実際のコンテンツタイプを提供している場合、そこから作 成した値になります。この値は辞書型です。 disposition content-disposition ヘッダの最初の部分の値です。 disposition_options content-disposition ヘッダが辞書形式で二つめの部分がある場合、その値です。 参考資料: RFC 1867, “Form-based File Upload in HTML” フォームベースのファイルアップロードに関する解説 4.6.3 その他の関数 parse_qs(qs[, keep_blank_values, strict_parsing ]) この関数は機能的に標準ライブラリの cgi モジュールにおける parse_qs と同じです。ただしこち らは C で書かれていてはるかに高速です。 引数に指定したクエリ文字列 (application/x-www-form-urlencoded 型のデータ) を解析し、データを辞 書の形にして返します。辞書のキーは一意なクエリ変数名で、辞書の値は各名前に対応する値のリス トです。 オプションの引数 keep_blank_values はフラグで、URL エンコードされたクエリ中の空白の値を空文 字列として扱うかどうかを指示するフラグです。値を真にすると、空白の値は空文字列のまま残りま す。デフォルトの値は偽で、この場合空白の値は無視され、クエリに存在しなかったかのように扱わ れます。 注意: strict_pasrsing 引数はまだ実装されていません。 35 parse_qsl(qs[, keep_blank_values, strict_parsing ]) この関数は機能的に標準ライブラリの cgi モジュールにおける parse_qsl と同じです。ただしこ ちらは C で書かれていてはるかに高速です。 引数に指定したクエリ文字列 (application/x-www-form-urlencoded 型のデータ) を解析し、データを辞 書の形にして返します。辞書のキーは一意なクエリ変数名で、辞書の値は各名前に対応する値のリス トです。 オプションの引数 keep_blank_values はフラグで、URL エンコードされたクエリ中の空白の値を空文 字列として扱うかどうかを指示するフラグです。値を真にすると、空白の値は空文字列のまま残りま す。デフォルトの値は偽で、この場合空白の値は無視され、クエリに存在しなかったかのように扱わ れます。 注意: strict_pasrsing 引数はまだ実装されていません。 redirect(req, location[, permanent=0, text=None ]) ブ ラ ウ ザ を 他 の 場 所 に リ ダ イ レ ク ト す る た め の 便 宜 関 数 で す。permanent が 真 の 場 合 、 MOVED_PERMANENTLY ス テ ー タ ス コ ー ド を ク ラ イ ア ン ト に 送 信 し ま す。そ れ 以 外 の 場 合 、 MOVED_TEMPORARILY を送信します。(リダイレクトをサポートしないごく少数のブラウザ向けに) ドキュメントが移動してしまったことを知らせる短いテキストを送信します。このテキストの内容 は text でオーバライドできます。 ヘッダをすでに送信した後にこの関数を呼び出すよ、IOError を送出します。 この関数は apache.SERVER_RETURN 例外を送出して、ハンドラの処理をこれ以上行わないように します。この振舞いが望ましくない場合、redirect への呼び出しを try/except ブロックでラップし て、apache.SERVER_RETURN を捕捉してください。 4.7 Cookie – HTTP 状態管理 Cookie モジュールでは、Netscape の発表した仕様で定義されている HTTP クッキーの生成と解析、およ び送受信を簡単に行う方法を提供しています。 注意: クッキーを使った HTTP 状態管理について説明している公式の IETF RFC は実在するものの、ほ とんどのブラウザがサポートしているデファクトスタンダードはもとの Netscape 仕様です。さらに、 IETF の標準に完全に準拠した場合、RFC 準拠であるにもかかわらず広く使われている多くのブラウザと互換性 がなくなってしまいます。そこで、このモジュールでは現在実際に広く使われている仕様を使います。完 全な RFC 準拠ではありません。 参考資料: Persistent Client State - HTTP Cookies (http://wp.netscape.com/newsref/std/cookie_spec.html) Netscape によるオリジナルの仕様です。 RFC 2109, “HTTP State Management Mechanism” クッキーに関する最初の RFC です。 RFC 2964, “Use of HTTP State Management” クッキーの用法についてのガイドラインです。 RFC 2965, “HTTP State Management Mechanism” 最新の IETF 標準です。 HTTP Cookies: Standards, Privacy, and Politics (http://arxiv.org/abs/cs.SE/0105018) David M. Kristol による、クッキーの標準化にまつわる諸問題について概観した素晴らしい文献です。 36 4.7.1 クラス class Cookie(name, value[, attributes ]) このクラスは name という名前を持ち、value を値に持つ単一のクッキーを生成するために使います。 加えて、Netscape の仕様と RFC2109 で定義されている属性もキーワード引数で渡せます。 オブジェクトの属性はクッキーの属性を表しており、その文字列表記はクッキーの文字列表記の一部 になります。Cookie クラスは属性名を有効なものだけに制限していて、実際には name, value, version, path, domain, secure, comment, expires, max_age, commentURL, discard, port, __data__ だけが許されています。 __data__ 属性は汎用の辞書で、必要に応じて任意の値を入れられます (Cookie をサブクラス化す る場合に便利です)。 expires 属性の値は、設定時に ‘Wdy, DD-Mon-YYYY HH:MM:SS GMT’ の形式 (Netscape のクッ キー仕様で定義されている形式) またはエポックからの経過秒数 (この値は自動的に GMT における正 しい時刻文字列に変換されます) のいずれかになっているかチェックを受けます。不正な expire 値 を指定すると ValueError を送出します。 Cookie オブジェクトを文字列に変換すると、‘Cookie’ や ‘Set-Cookie’ ヘッダの値として使え る正しい形式の文字列になります。 注意: Python 標準ライブラリの Cookie クラスと違い、このクラスは単一のクッキーを表現してい ます (Python 標準ライブラリでは Morsel に相当します)。 parse(string) この関数は、ヘッダの値として渡されるクッキー表現文字列 (cookie string) string から Cookie インスタンスを生成する際に使うクラスメソッドです。クッキー表現文字列の解析の過程で、各 属性名は全て小文字に変換されます。 このメソッドはクラスメソッドなので、クラス名を明示的に指定して呼び出さねばなりません。 このメソッドは単一の Cookie インスタンスではなく Cookie インスタンスの入った辞書を返 します。 単一の Cookie インスタンスをクッキー表現文字列から取り出す例を以下に示します: mycookies = Cookie.parse("spam=eggs; expires=Sat, 14-Jun-2003 02:42:36 GMT") spamcookie = mycookies["spam"] 注意: このメソッドでは辞書を使っているため、名前の重複する複数のクッキーは扱えません。一 つのクッキーに複数の値を持たせたい場合には、MarshalCookie の使用を検討してください。 class SignedCookie(name, value, secret[, attributes ]) Cookie のサブクラスです。このクラスで生成されるクッキーの名前と値は、secret に指定した暗号 鍵を使って自動的に署名されます。secret を空文字列にしてはなりません。 parse(string, secret) Cookie.parse() と同じようにふるまいますが、クッキーが正しく署名されているかどうか を調べます。署名を検証できない場合、Cookie クラスのオブジェクトを返します。 注意: SignedCookie.parse() の返すオブジェクトの型は必ず調べるようにしてください。 オブジェクトが Cookie のインスタンスである場合 (SignedCookie でない場合)、署名の検 証に失敗しています: 37 # assume spam is supposed to be a signed cookie if type(spam) is not Cookie.SignedCookie: # do something that indicates cookie isn’t signed correctly class MarshalCookie(name, value, secret[, attributes ]) SignedCookie のサブクラスです。このクラスを使うと value を任意の整列化 (marshal) 可能なオブ ジェクトにできます。文字列、整数、リストなどといったコアの Python データ型は全て整列化でき ます。整列化できるオブジェクトの完全なリストは marshal モジュールのドキュメントを参照してく ださい。 クッキー解析時、まず署名をチェックします。従って、正しく署名されていないクッキーは整列化状 態から戻せません。 4.7.2 関数 add_cookie(req, cookie[, value, attributes ]) クッキーをリクエストヘッダに設定するための便宜関数です。req は mod_python の Request オ ブジェクトです。cookie が Cookie (またはそのサブクラス) のインスタンスならばクッキーをセッ トします。そうでない場合には cookie は文字列でなければならず、cookie を名前に、value を値に持 ち、かつキーワード引数に指定した有効な Cookie の属性を持つような Cookie が構築されます。 また、この関数は ‘Cache-Control: no-cache="set-cookie"’ ヘッダを設定して、クッキー の値をキャッシュしないようキャッシュメカニズムに伝えます。 関数の使い方を一つ紹介します: c = Cookie.Cookie(’spam’, ’eggs’, expires=time.time()+300) Cookie.add_cookie(req, c) 別の使い方です: Cookie.add_cookie(req, ’spam’, ’eggs’, expires=time.time()+300) get_cookies(req [, Class, data ]) サーバに届いたヘッダからクッキーを取得するための便宜関数です。req は mod_python の Request オブジェクトです。Class は parse() メソッドでクッキーを解析する時に使うクラスで、デフォル トは Cookie です。data は任意の数のキーワード引数で、parse() に渡されます (parse の追加引 数として secret が必要な SignedCookie や MarshalCookie クラスの場合に便利です)。 4.7.3 例 以下の例では、300 秒で期限の切れる単純なクッキーをセットしています: 38 from mod_python import Cookie, apache import time def handler(req): cookie = Cookie.Cookie(’eggs’, ’spam’) cookie.expires = time.time() + 300 Cookie.add_cookie(req, cookie) req.write(’This response contains a cookie!\n’) return apache.OK この例では、サーバに marshal クッキーが送信されていないか調べ、送信されていればその内容をクラ イアントに送信します。クッキーが送信されていない場合、新たな marshal クッキーをセットします。この 例では ‘secret007’ を HMAC 署名の暗号鍵に使っています。 from mod_python import apache, Cookie def handler(req): cookies = Cookie.get_cookie(req, Cookie.MarshalCookie, secret=’secret007’) if cookies.has_key(’spam’): spamcookie = cookies[’spam’] req.write(’Great, a spam cookie was found: %s\n’ \ % str(spamcookie)) if type(spamcookie) is Cookie.MarshalCookie: req.write(’Here is what it looks like decoded: %s=%s\n’ % (spamcookie.name, spamcookie.value)) else: req.write(’WARNING: The cookie found is not a \ MarshalCookie, it may have been tapered with!’) else: # MarshaCookie allows value to be any marshallable object value = {’egg_count’: 32, ’color’: ’white’} Cookie.add_cookie(req, Cookie.MarshalCookie(’spam’, value, \ ’secret007’)) req.write(’Spam cookie not found, but we just set one!\n’) return apache.OK 4.8 Session – セッション管理 Session モジュールでは、リクエスト間をまたぐ持続性 (persistent) のセッションを管理するオブジェク トを提供しています。 このモジュールには BaseSession クラスと DbmSession クラスが入っています。BaseSession は 直接使うためのクラスではありません (セッションを保存する手段を持っていません)。一方、DbmSession クラスは dbm を使ってセッションを保存します。 BaseSession クラスでは、プロセス間やスレッド間でセッションをロックするメカニズムも提供して います。ロックには APR の global_mutex を使います (このロックは起動時にあらかじめ複数作成され 39 ます)。 mutex の識別番号はセッション id の法 (modulus) を使って (hash() で) 計算されます。(従って、 異なるセッション id が同じハッシュ値になる可能性はあります。しかし、結果的には、これら二つのセッ ションが同時にロックされることはあり得ないため、わずかな処理の遅延が起こるにすぎません。 4.8.1 Classes Session(req[, sid, secret, timeout, lock, lockfile ]) この関数は MPM に問い合わせを行い、その結果に基づいて DbmSession か MemorySession の いずれかの新たなインスタンスを返します。この関数は BaseSession と同じ引数を取ります。 MPM がスレッドで動作しており、かつ fork していない場合 (Windows のようなケースです) や、ス レッドで動作し fork してはいるがプロセス数が一つに制限されている場合 (このように動作させるに は作業者 MPM (worker MPM) を設定します) 場合には MemorySession が使われます。それ以外の 場合には DbmSession が使われます。 class BaseSession(req[, sid, secret, timeout, lock, lockfile ]) このクラスは、実際のセッション保存メカニズムを実装している別のクラスの基底クラスとして使わ れています。req は必須の引数で、mod_python のリクエストオブジェクトを参照していなければな りません。 BaseSession は dict のサブクラスです。セッションを辞書として使うことで、データをセッショ ンに保存したりセッションから取り出したりできます。 sid はオプションのセッション id です。この値を指定する場合、該当するセッションが既に実在して いなければなりません。該当するセッションが実在しない場合、sid は無視され、新たなセッション id を持ったセッションが生成されます。sid を指定しなかった場合、オブジェクトはクッキーの中か らセッション id を捜し出そうとします。クッキー中にセッション id が見つかったが、未知のセッショ ン id だったりセッションが期限切れになっている場合、新たなセッション id を作成します。セッショ ンが「新しい (new)」かどうかは、is_new() メソッドを呼び出せば判別できます。 セッション で 作 成 し た クッキ ー は 、サ ー バ の DocumentRoot と 現 在 の 処 理 を 担 当 し て い る PythonHandler ディレクティブの場所を比較して算出した path 属性を持ちます。例えば、ドキュ メントルートが ‘/a/b/c’ で PythonHandler を指定した場所が ‘/a/b/c/d/e’ の場合、path は ‘/d/e’ にな ります。ApplicationPath オプションを使うと、特定のパス名を強制的に指定できます (サーバ 設定内で、‘PythonOption ApplicationPath /my/path’ のように指定します)。 secret を指定した場合、BaseSession はクッキー生成時に SignedCookie を使い、セッション id の偽造をほぼ不可能にします。デフォルトでは通常の Cookie を使います (たとえ署名されていなく ても、セッション id は非常に推測しにくい方法で生成されています)。 セッションは、timeout 以上の間アクセスを受けないとタイムアウトします。timeout のデフォルト値は 30 分です。期限切れのセッションにアクセスしようとすると「新たな」セッションが開始されます。 lock 引数はロックを行うかどうかを表します (デフォルト値は 1 です)。ロックが有効になっている場 合、一度に一つのセッション id について一つのセッションオブジェクトしかインスタンス化できま せん。このとき、lockfile はプロセス間のロックに使うファイルの名前です。 セッション id が新たに生成された状態では、セッションは「新しい」といいます。その逆は、セッ ション id をクッキーや sid 引数で指定した場合です。 is_new() セッションが新しい場合、1 を返します。期限切れのセッションや実在しないセッションのイン スタンスを生成しようとした場合にも、 「新しい」セッションになります。例えば以下のように、 セッションのインスタンスを生成する試みが成功したかどうかを調べる場合にはこのメソッド が重要になります: 40 sess = Session(req) if sess.is_new(): # redirect to login util.redirect(req, ’http://www.mysite.com/login’) id() セッションの id を返します。 created() セッションの生成時刻をエポック (epoch) からの経過秒数で返します。 last_accessed() セッションの最終あ q 癖素時刻をエポックからの経過秒数で返します。 timeout() セッションのタイムアウト期限を秒数で返します。 set_timeout(secs) セッションのタイムアウト期限を secs 秒にします。 invalidate() セッションを永続化ストレージ (persistent store) から削除し、該当するセッション id のクッキー を無効化するためのヘッダを出力ヘッダ部に配置します。 load() セッションの値をストレージから読み出します。 save() セッションの値をストレージに書き込みます。 delete() セッションをストレージから削除します。 init_lock() セッションのロックを初期化します。このメソッドを呼び出す必要はありません。このメソッド が定義されているのはサブクラスで別のロックメカニズムを使いたい場合のためです。 lock() セッションをロックします。他のスレッドやプロセスがすでにセッションをロックしている場 合、ロックが解除されるまで待機します。ロックを自動的に制御している場合 (デフォルトの場 合) には、このメソッドを呼び出す必要はありません。 このメソッドはリクエスト処理の終了時に常にセッションのロックを解除するような後処理関 数を登録します。 unlock() セッションのロックを解除します (lock() と同じく、ロックが自動制御 (デフォルト) の場合に は、このメソッドを呼び出す必要はありません)。 cleanup() サブクラスでセッションストレージを消去 (期限切れのセッションを削除するなど) するメカ ニズムを実装するためのメソッドです。このメソッドはランダムに呼び出されることになり、 呼び出しの起きる確率は Session モジュールの変数 CLEANUP_CHANCE で制御されていま す (デフォルトの値は 1000 です)。この値は、ストレージの消去がランダムに発生し、その確 率が 1000 分の 1 であることを意味します。サブクラスでこのメソッドを実装する場合、必ず しも (時間のかかる) 消去処理をこのメソッド内で行わなくても構いませんが、その場合には 41 req.register_cleanup を使ってリクエストの処理後に実行される後処理関数を登録してく ださい。 class MemorySession(req, [, sid, secret, dbmtype, timeout, lock ]) このクラスでは、グローバル名前空間上の辞書を使ったセッションストレージを提供しています。こ のクラスはパフォーマンスの点では最高ですが、マルチプロセスの設定では利用できず、アクティブ なセッションができるたびにメモリを消費するという問題があります。 このクラスを直接使うとクロスプラットフォームにならないので注意してください。できるだけプ ラットフォーム間での互換性をもたせたければ、常に Session() を使うようにしてください。 class DbmSession(req, [, dbm, sid, secret, dbmtype, timeout, lock ]) このクラスでは、dbm ファイルを使ったセッションストレージを提供しています。dbm のアクセスは 非常に高速で、ほとんどの dbm 実装ではメモリマップファイルを使って高速化を実現しているため、 ほぼ共有メモリアクセスに近いパフォーマンスを出せます。 dbm は dbm ファイルの名前です (ファイルは httpd プロセスから読み書きできなければなりませ ん) このファイルはサーバプロセスが停止しても削除されません (このため、サーバが再起動し てもセッションは死なないという便利な副作用があります)。デフォルトでは、セッション情報は tempfile.gettempdir() 標準ライブラリ関数の返す一時ディレクトリ下の ‘mp_sess.dbm’ とい うファイルになります。このデフォルト値は PythonOption SessionDbm filename を設定し てオーバライドできます。 実装では Python の anydbm モジュールを使っています。このモジュールは、デフォルトではほとん どのシステムで dbhash を使います。特定の dbm 実装 (例えば gdbm) を使いたければ、dbmtype に モジュールを渡せます。 このクラスを直接使うとクロスプラットフォームにならないので注意してください。できるだけプ ラットフォーム間での互換性をもたせたければ、常に Session() を使うようにしてください。 4.9 psp – Python Server Pages psp モジュールでは、特別なブラケット表記中に Python コードを埋め込んだテキストドキュメント (HTML も含みますが、それだけではありません) を変換して、 mod_python ハンドラ内で実行できるような適切 な形式の pure Python コードにします。これにより、ASP や JSP などといった動的なコンテンツを配信す るための融通のきくメカニズムを実現しています。 psp の使っているパーザは (flex で生成した) C で書かれているため、非常に高速に動作します。 PSP の詳細については 6.2 節「PSP ハンドラ」を参照してください。 ドキュメント中の Python のコード (code) は ‘<%’ と ‘%>’ で囲わねばなりません。Python の式 (expression) は ‘<%=’ と ‘%>’ で囲みます。ディレクティブ (directive) は ‘<%@’ と ‘%>’ で囲みます。コメント (処理後の コード中には入りません) は ‘<%-’ と ‘-%>’ で囲います。 コードと式の両方をを HTML ドキュメントに埋め込んでいる簡単な PSP ページを以下に示します: <html> <% import time %> Hello world, the time is: <%=time.strftime("%Y-%m-%d, %H:%M:%S")%> </html> 内部では、PSP パーザが上のページを以下の Python コードに翻訳します: 42 req.write("""<html> """) import time req.write(""" Hello world, the time is: """); req.write(str(time.strftime("%Y-%m-%d, %H:%M:%S"))); req.wr </html> """) このコードをハンドラ内で実行すると、‘Hello world, the time is:’ の後ろに現在の時刻が入っ たページになります。 Python コードを使って、条件やループでページの一部分を表示できます。Python コード内のブロックは インデントで表現されます。ある Python コードブロックの最後の行のインデントは (コメント文であって も) ドキュメントの末尾か、次の Python コードブロックまで持続します。 例を示します: <html> <% for n in range(3): # This indent will %> <p>This paragraph will repeated 3 times.</p> <% # This line will cause %> This line will only be </html> persist be the block to end shown once.<br> 上の例は、内部的には以下の Python コードになります: req.write("""<html> """) for n in range(3): # This indent will req.write(""" <p>This paragraph will repeated 3 times.</p> """) # This line will cause req.write(""" This line will only be </html> """) persist be the block to end shown once.<br> パーザは賢くて、Python コードブロックの最後の行が ‘:’ (コロン) で終っている場合にも正しくインデ ントを推測します。このことと、‘<%%>’ の中で改行に到達するとインデントはリセットされることを考慮 すると、上のページは以下のようにも書けます: 43 <html> <% for n in range(3): %> <p>This paragraph will be repeated 3 times.</p> <% %> This line will only be shown once.<br> </html> とはいえ、このコードには困惑させられるでしょう。ですから、行儀として、ブロック間で説明用のコ メントを入れておくよう強く勧めます。 現時点でサポートしているディレクティブは include だけで、以下のようにして使います: <%@ include file="/file/to/include"%> parse() 関数を dir 引数つきで呼び出す場合には、‘file’ は相対パスで指定できます。それ以外の場 合には絶対パスでなければなりません。 class PSP(req, [, filename, string, vars ]) PSP オブジェクトを表すクラスです。 req はリクエストオブジェクトです; filename および string はオプションのキーワード引数で、 PSP コードのソースを表します。これらの変数はいずれか一つだけを指定できます。両方とも指定しな かった場合、filename に req.filename を使います。 vars はグローバル変数の辞書です。run() メソッドに変数を渡すと、ここで渡した vars の内容をオー バライドします。 このクラスは PSP ハンドラが内部で利用しますが、汎用のテンプレートツールとしても使えます。 ファイルをソースに使う場合、特定のファイルから得られるコードオブジェクトはファイル名と更新 時刻をキーにしてメモリキャッシュ上に保存されます。キャッシュは Python インタプリタ上のグロー バルな値です。従って、ファイル更新時刻が変わらない限り、ファイルのパーズとコードオブジェク トのコンパイルはインタプリタごとに一度しか行いません。 キャッシュされたページのサイズが時としてかなりのメモリを消費することがあるため、キャッシュの サイズは 512 ページに制限されています。メモリの消費量が問題になる場合は dbm によるファイル キャッシュに切替えられます。bsd db を使った簡単なテストでは、速度はわずか 20% しか低下しませ んでした。dbm ライブラリによってはレコードエントリのサイズに制限を課しているために利用でき ないことがあります。そのため、自分のシステムでの anydbm のデフォルトの実装が何かを調べてお く必要があるでしょう。dbm によるキャッシュは ‘PythonOption’ ディレクティブに PSPDbmCache を指定して有効にします: PythonOption PSPDbmCache ‘‘/tmp/pspcache.dbm’’ dbm キャッシュファイルはサーバの再起動時に削除されないので注意してください。 ファイルと違い、文字列から生成したコードオブジェクトはメモリ上にしかキャッシュされません。 現時点では、dbm ファイル上にキャッシュするという選択肢はありません。 run([vars ]) このメソッドは (初期化時に PSP ソースを解析/コンパイルしてできた) コードを実行します。 オプションの引数 vars は文字列をキーにした辞書で、グローバル変数の辞書として渡されます。 44 さらに、PSP コードには req, psp, session および form といったグローバル変数が渡さ れます。コード中で session を参照している場合に限り、セッション情報が生成され、変数 session に代入されます (PSP ハンドラはコードオブジェクトの co_names を調べて session への参照の有無を判定します)。session を一度でも参照すると、好むと好まざるとにかかわら ず session はクッキーを生成してセッションのロックを開始するということを覚えておいてくだ さい。同様に、form がコード中で参照されていると、mod_python クラスの FieldStorage オブジェクトがインスタンス化されます。 psp には PSPInstance のインスタンスが渡されます。 display_code() 元の PSP コードと PSP パーザが生成した Python コードのリストを横に並べたような形に HTML でフォーマットし、文字列にして返します。 PSP をテンプレートメカニズムとして使う例を以下に示します: テンプレートファイルは以下のようになります: <html> <!-- This is a simple psp template called template.html --> <h1>Hello, <%=what%>!</h1> </html> ハンドラコードは以下のようになります: from mod_python import apache, psp def handler(req): template = psp.PSP(req, filename=’template.html’) template.run({’what’:’world’}) return apache.OK class PSPInstance() グローバル変数 psp として PSP コードに渡されるオブジェクトのクラスです。このクラスのインス タンスは内部的にインスタンス化されることになっています。__init__ のインタフェースはわざ とドキュメント化していません。 set_error_page(filename) 例外が生じたときに処理される psp ページを設定するために使います。filename が絶対パス表記 の場合、ドキュメントルートの後ろに追加されます。それ以外の場合は現在のページと同じディ レクトリにあるものとみなします。エラーページは sys.exc_info() の返す三要素のタプル を引数 exception で受け取ります。 apply_data(object[, **kw ]) このメソッドはフォームデータをキーワード引数にして呼び出し可能オブジェクト object を呼 び出し、その結果を返します。 redirect(location[, permanent=0 ]) こ の メ ソッド は ブ ラ ウ ザ を location に リ ダ イ レ ク ト さ せ ま す。permanent が 真 の 場 合 、 MOVED_PERMANENTLY を送信します (そうでない場合には MOVED_TEMPORARILY を送信し ます)。 注意: リダイレクトを起こせるのはクライアントにデータを送信する前だけです。従って、この メソッドを呼び出す Python コードブロックはページの先頭になければなりません。そうでない 場合、IOError が送出されます。 45 例: <% # note that the ’<’ above is the first byte of the page! psp.redirect(’http://www.modpython.org’) %> この他に、 psp では以下の低水準関数を提供しています: parse(filename[, dir ]) この関数は filename という名前のファイルを開き、内容を読み出して解析し、得られた Python コー ドの入った文字列を返します。 dir を指定すると、解析対象となるファイルの完全な名前を dir と filename を使って決め、include ディレクティブの引数を相対パスとして指定できるようになります。(ファイル名は単に二つの変数 をつなげて作成するため、dir の末尾にパス区切り文字が入っていなくても補完されないので注意し てください)。 parsestring(string) この関数は string の内容を解析し、得られた Python コードを文字列で返します。 46 五章 Apache 設定ディレクティブ 5.1 Request Handlers 5.1.1 Python*Handler ディレクティブ構文 リクエストハンドラのディレクティブは全て次のような構文になります: Python*Handler handler [handler ...] [ | .ext [.ext ...] ] handler は呼び出し可能オブジェクトで、単一の引数、リクエストオブジェクトをとります。.ext はファ イルの拡張子です。 一つの行には複数のハンドラを指定できます。この場合、各ハンドラは左から右へ順に呼ばれてゆきま す。同じハンドラの設定を複数回指定でき、これも同じ結果を返します; すなわち、全てのハンドラが最初 から最後まで順次実行されてゆきます。ハンドラ列のうちいずれかが apache.OK 以外の値を返すと、そ れ以降のハンドラの実行は中止されます。 ハンドラリストの後ろには、|を続け、その後ろにファイル拡張子を複数個指定できます。この指定を行 うと、該当するファイル拡張子に対してのみハンドラが実行されるよう制限します。この機能は trans フェ イズ後に実行されるハンドラにのみ適用されます。 handler は次のような文法になります: module[::object] module は完全なモジュール名 (パッケージ名のドット表記も利用できます) で、オプションの object はモ ジュール内のオブジェクト名です。 オブジェクトの指定にもドット (.) を含められます。この場合、左から右に名前解決を行ってゆきます。 解決処理中に<class>型のオブジェクトに遭遇すると、mod_python はリクエストオブジェクトを引数 にしてクラスのインスタンスを生成しようとします。 オブジェクト名を指定しない場合、ハンドラのディレクティブ名をすべて小文字にし、先頭の ‘python’ を除いた名前をデフォルトとして使います。例えば、‘PythonAuthenHandler’ に対するデフォルトのオ ブジェクト名は ‘authenhandler’ になります。 例: PythonAuthzHandler mypackage.mymodule::checkallowed ハンドラの詳細については「ハンドラの概要」を参照してください。 補足: ‘::’ を使っているのはパフォーマンス上の理由からです。Python は、モジュール内のオブジェク トを使用する場合、まず最初にモジュールを import しておく必要があります。区切りを単に ‘.’ にしてし まうと、各語がパッケージ、モジュール、クラス等のいずれかを順に決めるプロセスがかなり複雑になって しまいます。そこで、(Python らしくない)‘::’ を使うことで、モジュールを指定する部分がどこで終わり、 モジュール内のオブジェクトの指定がどこから始まるのかを判定するためにかかる時間を mod_python か 47 ら省き、パフォーマンスを十分に稼いでいるのです。 5.1.2 PythonPostReadRequestHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト オーバライド: not None モジュール: mod_python.c このハンドラは、リクエストの内容を読み込み終わった後で、かつ他のフェイズの処理が行われるより も前に呼び出されます。このハンドラは入力ヘッダフィールドに基づいて何らかの判定を行う場合に便利 です。 注 意: こ の リ ク エ ス ト 処 理 フェイ ズ で は 、URI は ま だ パ ス 名 に 変 換 さ れ て い ま せ ん 。従って 、 <Directory>や<Location>、<File> ディレクティブ内や ‘.htaccess’ ファイルの中で指定しても Apache はこのディレクティブを実行できません。このディレクティブを置けるのはメインの設定ファイルのみで、 そのコードはメインのインタプリタが実行します。また、このフェイズはリクエストのコンテキストタイ プ (例えば、要求が Python プログラムを指しているのか、それとも gif ファイルなのか) を特定するよりも 前に発生するので、このハンドラで指定した Python のルーチンは、このサーバに対するあらゆる (Python プログラム以外も含む) リクエストに対して呼び出されてしまいます。これはパフォーマンスが優先課題の 場合には熟慮すべき問題です。 以下のハンドラは、Apache が処理する各フェイズの順に合わせてドキュメント化されています。 5.1.3 PythonTransHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト オーバライド: not None モジュール: mod_python.c このハンドラは、サーバのデフォルトの規則 (Alias ディレクティブなど) に先だって URI から実際のファ イル名への変換を行う機会を与えるために呼び出されます。 注 意: こ の リ ク エ ス ト 処 理 フェイ ズ で は 、URI は ま だ パ ス 名 に 変 換 さ れ て い ま せ ん 。従って 、 <Directory>や<Location>、<File> ディレクティブ内や ‘.htaccess’ ファイルの中で指定しても Apache はこのディレクティブを実行できません。このディレクティブを置けるのはメインの設定ファイルのみで、 そのコードはメインのインタプリタが実行します。 5.1.4 PythonHeaderParserHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c このハンドラは、リクエストの処理手順の早期でリクエストヘッダを調べ、何らかの適切な動作を行う 機会を与えるために呼び出されます。 5.1.5 PythonInitHandler 文法: Python*Handler Syntax 48 コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c リクエスト処理フェイズの中で最初に呼び出されるハンドラで、‘.htaccess’ とディレクトリの内外で使 用できます。 このハンドラは、実際には異なる 2 つのハンドラの別名です。メイン設定ファイル中でディレクトリタグの 外に設定すると PostReadRequestHandler の別名になります。また、(PostReadRequestHandler を置けないような) ディレクトリタグの内側に設定したときには、PythonHeaderParserHandler の別 名になります。 (このアイデアは mod_perl をもとにしています) 5.1.6 PythonAccessHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c このルーチンは、リクエストされたリソースに対し、mod_python モジュール特有の制限をかけている かどうかを調べるために呼ばれます。 例えば、このハンドラを使って IP アドレスによる制限を行えます。アクセス不許可を示したければ、こ のハンドラで HTTP_FORBIDDEN などを返します。 5.1.7 PythonAuthenHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c このルーチンはリクエストと同時に送られてきた認証情報を (ユーザがデータベースの中にあるかどう か、また [暗号化された] パスワードがデータベース上のパスワードと一致しているかを調べるなどして) チェックするときに呼ばれます。 ユ ー ザ 名 を 得 る に は 、req.user を 使 い ま す。ま た 、ユ ー ザ が 入 力 し た パ ス ワ ー ド を 得 る に は req.get_basic_auth_pw() 関数を使います。 apache.OK を返すことで、認証の成功を意味します。apache.HTTP_UNAUTHORIZED を返すと大抵 のブラウザでは認証 (パスワード) ダイアログが再度表示されます。apache.HTTP_FORBIDDEN を返すと エラーを表示し、認証 (パスワード) ダイアログは表示されません。認証は成功しているものの、ユーザが 特定の URL にアクセスを許されていない場合には HTTP_FORBIDDEN を使ってください。 認証ハンドラの例は以下のようになります: 49 def authenhandler(req): pw = req.get_basic_auth_pw() user = req.user if user == "spam" and pw == "eggs": return apache.OK else: return apache.HTTP_UNAUTHORIZED 注意: req.get_basic_auth_pw() は req.user の値を使う前に呼び出さねばなりません。Apache は req.get_basic_auth_pw() を呼び出すまで認証情報のデコードを試みません。 5.1.8 PythonAuthzHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c このハンドラは ‘AuthenHandler’ の後に実行されます。ユーザが特定のリソースにアクセスできるか を調べる為のハンドラです。とはいえ、この処理はしばしば ‘AuthenHandler’ そのものの中で行われ ます。 5.1.9 PythonTypeHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c このルーチンは、(r->content_type から判る) Content-type、言語などといった様々なドキュメント タイプ情報を決定したり設定したりするために呼び出されます。 5.1.10 PythonFixupHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c このルーチンはヘッダフィールド等の固定を mod_python モジュール特有の方法で実行するために呼 び出されます。このハンドラはコンテンツハンドラを呼び出す直前に起動されます。 5.1.11 PythonHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c 50 メインのリクエストハンドラです。多くのアプリケーションではこのハンドラだけを指定することにな るでしょう。 5.1.12 PythonLogHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c このルーチンは mod_python 特有のログ処理を実現するために呼び出されます。 5.1.13 PythonCleanupHandler 文法: Python*Handler Syntax コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c これは一番最後のハンドラで、Apache がリクエストオブジェクトを捨てる直前に呼ばれます。 他のハンドラと違い、戻り値は無視されます。エラーはエラーログに書きこまれますが、‘PythonDebug’ が ‘On’ になっていてもクライアントには何も送信されません。 このハンドラは rec.add_handler() 関数の引数には使えません。動的な後処理 (cleanup) を登録する には、req.register_cleanup() を使います。 一度後処理が始まると、それ以上後処理は登録できません。そのため、req.register_cleanup() はこのハンドラ内では効果がありません。 このディレクティブで登録したハンドラは、req.register_cleanup() で登録した後処理ハンドラ よりも後に実行されます。 5.2 5.2.1 フィルタ PythonInputFilter 文法: PythonInputFilter handler name コンテキスト: サーバ設定 モジュール: mod_python.c name という名前で入力フィルタ handler を登録します。Handler はモジュール名で、::のあとに続けて 呼出し可能オブジェクト名をつけられます。呼出し可能オブジェクトの名前を省略した場合、デフォルト で ‘inputfilter’ になります。慣例で Name に登録するフィルタ名は大抵は全て大文字にします。 フィルタを有効にするには AddInputFilter を設定します。 5.2.2 PythonOutputFilter 文法: PythonOutputFilter handler name コンテキスト: サーバ設定 モジュール: mod_python.c 51 name という名前で出力フィルタ handler を登録します。Handler はモジュール名で、::のあとに続けて 呼出し可能オブジェクト名をつけられます。呼出し可能オブジェクトの名前を省略した場合、デフォルト で ‘outputfilter’ になります。慣例で Name に登録するフィルタ名は大抵は全て大文字にします。 フィルタを有効にするには AddOutputFilter を設定します。 5.3 5.3.1 接続ハンドラ PythonConnectionHandler 文法: PythonConnectionHandler handler コンテキスト: サーバ設定 モジュール: mod_python.c 接続を接続ハンドラ handler で処理するよう指定します。Handler には単一の引数、接続オブジェクトが 渡されます。 Handler はモジュール名で、::のあとに続けて呼出し可能オブジェクト名をつけられます。呼出し可能 オブジェクトの名前を省略した場合、デフォルトで ‘connectionhandler’ になります。 5.4 5.4.1 その他のディレクティブ PythonEnablePdb 文法: PythonEnablePdb {On, Off} Default: PythonEnablePdb Off コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c ‘On’ の場合、mod_python は pdb.runcall() 関数を使って Python デバッガ pdb の下でハンドラを 実行します。 pdb は対話的なツールなので、このディレクティブを使う場合には-DONE_PROCESS オプション付きで httpd を起動してください。ハンドラコードに到達するとすぐに pdb のプロンプトが表示され、コードをス テップごとに進めて変数を調べられるはずです。 5.4.2 PythonDebug 文法: PythonDebug {On, Off} Default: PythonDebug Off コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c 通常、捕捉されなかった Python のエラーによるトレースバック出力はログに送られます。PythonDebug を On に設定すると、IOError が出るまではトレースバックの出力は (ログの他に) クライアントにも送信 されます。ただし、出力時の IOError の場合にはエラーログだけに送信されます。 このディレクティブは開発時に非常に有用です。しかし、このディレクティブを使うと、意に反した、場 合によっては重大なセキュリティ上の情報をクライアントに暴露してしまう恐れがあるので、実運用では 使わないよう勧めます。 52 5.4.3 PythonImport 文法: PythonImport module interpreter_name コンテキスト: サーバ設定 モジュール: mod_python.c ‘interpreter_name’ に指定した名前を持つインタプリタの下で Python モジュール ‘module’ を import するようサーバに指示します。例えばデータベース接続の初期化など、時間がかかり、リクエスト処理時 に実行するのが望ましくない初期化タスクに便利です。 import は子プロセスの初期化時に起こるので、実際には起動した子プロセスあたり一度だけモジュール の import が起こります。 注意: import が起きるとき、設定は完全に読み込まれてはいないため、PythonInterpreter を含む他の全て のディレクティブはこのディレクティブで import したモジュールに影響を及ぼしません。この制限のため、 インタプリタ名を明示的に指定しておき、このディレクティブの操作に依存する後続のリクエストが実行 されるインタプリタの名前と一致させておかねばならないのです。実行しているインタプリタの名前が判 らないのなら、リクエストオブジェクトの interpreter メンバを調べてください。 多重インタプリタ (Multiple Interpreters) を参照して下さい。 5.4.4 PythonInterpPerDirectory 文法: PythonInterpPerDirectory {On, Off} Default: PythonInterpPerDirectory Off コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c インタプリタの名前を、サーバ名ではなくリクエスト中のファイルのディレクトリ名 (req.filename) からつけるよう mod_python に指示します。これは、デフォルトポリシの場合には、たとえ違うディレク トリ上であっても同じ仮想サーバ上では同じサブリンタプリタでスクリプトを実行するのに対し、異なる ディレクトリにあるスクリプトは互いに別個のサブインタプリタで実行されることを意味します。 例えば、‘/directory/subdirectory’ があると仮定します。‘/directory’ には PythonHandler ディレクティブを含 んだ.htaccess ファイルがあり、‘/directory/subdirectory’ には.htaccess がないとします。‘/directory’ の下のス クリプトと ‘/directory/subdirectory’ の下のスクリプトは、同じ仮想サーバを介してアクセスされていれば、 同じインタプリタ下で実行されます。PythonInterpPerDirectory が有効な場合は各ディレクトリ毎に別個の 二つのインタプリタになります。 注意: URI の変換より前の早い段階のリクエスト処理フェイズ (PostReadRequestHandler や TransHandler) では URI がまだ変換されていないため、パスがまだ決まっていません。その間は PythonInterpPerDirectory が On であったとしてもメインのインタプリタによって実行されます。この動作は期待しているものとは異 るかもしれません。が、この問題を会費する方法は残念ながらありません。 参考資料: 4.1 節、複数のインタプリタ (pyapi-interps.html) 詳しい情報です 5.4.5 PythonInterpPerDirective 文法: PythonInterpPerDirective {On, Off} Default: PythonInterpPerDirective Off 53 コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c 有効な Python*Handler ディレクティブのあるディレクトリの名前を使ってサブインタプリタの名前をつ けるよう mod_python に指示します。 例えば、‘/directory/subdirectory’ があると仮定します。‘/directory’ には PythonHandler ディレクティブの入っ た.htaccess ファイルがあり、‘/directory/subdirectory’ には別の PythonHandler の入った.htaccess ファイルがあ るとします。デフォルトでは、同じ仮想サーバを介してアクセスされていれば、‘/directory’ の下のスクリプト と ‘/directory/subdirectory’ の下のスクリプトは同じインタプリタ下で実行されます。PythonInterpPerDirective が有効な場合、各ディレクティブ毎に別個の二つのインタプリタになります。 参考資料: 4.1 節、複数のインタプリタ (pyapi-interps.html) 詳しい情報です 5.4.6 PythonInterpreter 文法: PythonInterpreter name コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c デフォルトの動作や、PythonInterpPerDirectory や PythonInterpPerDirective ディレク ティブで指定した挙動を上書きする形で、インタプリタの名前を強制的に name にするよう mod_python に指示します。 このディレクティブを使うと、通常は別個のサブインタプリタ下で行われるスクリプトの実行を同じサ ブインタプリタ下で行えます。DocumentRoot の下で使うと、サーバ全体で一つのサブインタプリタを使う ようになります。 参考資料: 4.1 節、複数のインタプリタ (pyapi-interps.html) 詳しい情報です 5.4.7 PythonHandlerModule 文法: PythonHandlerModule module コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c PythonHandlerModule を使うと、複数の Python*Handler ディレクティブを代替できます。このハンドラ でモジュールを指定すると、そのモジュールにデフォルトのハンドラ関数名に一致する関数を探しだし、あ ればその関数を実行します。 例えばつぎのような設定だったとした場合: 54 PythonAutenHandler mymodule PythonHandler mymodule PythonLogHandler mymodule 単純にこのように書けます。 PythonHandlerModule mymodule 5.4.8 PythonAutoReload 文法: PythonAutoReload {On, Off} Default: PythonAutoReload On コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c ‘Off’ にセットすると、モジュールファイルの更新日時を調べないよう mod_python に指示します。 デフォルトでは、mod_python はファイルのタイムタンプをチェックし、モジュールファイルの更新時 刻が以前にインポート、またはリロードしたときよりも新しければそのモジュールをリロードします。こ うしてモジュールは自動的に再 import されるため、モジュールを更新するたびにサーバーを再起動させる 必要が無くなります。 自動リロードを無効にすると、いくらか処理時間を節約でき、わずかなパフォーマンス向上をもたらす ため、モジュールの変更が起こり得ない実運用での環境では便利です。 5.4.9 PythonOptimize 文法: PythonOptimize {On, Off} Default: PythonOptimize Off コンテキスト: サーバ設定 モジュール: mod_python.c Python の最適化を有効にします。Python の-O オプションと同じです。 5.4.10 PythonOption 文法: PythonOption key value コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess オーバライド: not None モジュール: mod_python.c キーと値のペアをテーブルに割当てます。これは、req.get_options() 関数で参照できます。設定 ファイル (‘httpd.conf’ や ‘.htaccess’ など) と Python プログラムの間で情報をやりとりするのに役立ちます。 5.4.11 PythonPath 文法: PythonPath path コンテキスト: サーバ設定、バーチャルホスト、ディレクトリ、 htaccess 55 オーバライド: not None モジュール: mod_python.c PythonPath ディレクティブは PythonPath をセットします。path は次のように、Python リストの表記法を 使い記述します。 PythonPath "[’/usr/local/lib/python2.0’, ’/usr/local/lib/site_python’, ’/some/other/place’] このディレクティブで設定した path は既存のパスに追加されるのではなく、新しく置き換わります。と はいえ、path の実際の値は ‘eval’ で評価されるので、path にディレクトリを追加したければ、以下のよ うに指定できます: PythonPath "sys.path+[’/mydir’]" mod_python は PythonPath に係わる eval の実行回数を最低限にしようとします。というのも、 eval は 低速で、特に毎回ヒットするたびに解析されるような ‘.htaccess’ ファイル内で指定した場合には強烈な悪 影響を及ぼすことがあるからです。mod_python は PythonPath ディレクティブの引数 (評価する前の状態) を覚えておき、値を評価する前に覚えておいた値と比較して、値が同じならば何も行いません。このため、 コード内で pythonpath を変更している場合には、パスの内容を何らかの値に戻すための手段をディレクティ ブに頼ってはなりません。 注意: このディレクティブはセキュリティ処置として使うべきではありません。Python パスはスクリプ トから簡単に操作できるためです。 56 六章 標準のハンドラ 6.1 publisher ハンドラ publisher ハンドラは、独自にハンドラを書かずに済ませ、迅速にアプリケーションを開発するための 手段です。Zope の ZPublisher に影響を受けています。 6.1.1 Introduction このハンドラを使うには、設定ファイルに以下のような行を追加します: <Directory /some/path> SetHandler mod_python PythonHandler mod_python.publisher </Directory> このハンドラを使うと、モジュール内の関数や変数を URL でアクセスできるようになります。例えば、 ‘hello.py’ という名の以下のようなモジュールがあるとしましょう: """ Publisher example """ def say(req, what="NOTHING"): return "I am saying %s" % what http://www.mysite.com/hello.py/say という URL は ‘I am saying NOTHING’ を返します。 一方 http://www.mysite.com/hello.py/say?what=hello は ‘I am saying hello’ を返しま す。 6.1.2 パブリッシュのアルゴリズム publisher ハンドラは URI を Python の変数や呼び出し可能オブジェクトに直接対応づけます。次に、変数 の場合には文字列表現を、呼び出しオブジェクトの場合には呼び出した戻り値の文字列表現をそれぞれ返 します。 トラバース publisher ハ ン ド ラ は URI に 指 定 さ れ た モ ジュー ル を 探 し て import し ま す。モ ジュー ル の 位 置 は req.filename 属性で決まります。ファイル拡張子がある場合、 import を行う前に無視します。 req.filename が空の場合、モジュール名はデフォルト値の ‘index’ になります。‘index’. 57 モジュールを import すると、URI の残りの部分からクエリデータ (いわゆる PATH_INFO) までの間の部 分を使ってモジュール内のオブジェクトを探します。publisher ハンドラは各要素をモジュール内の Python オブジェクトに対応づけながら、パスの要素を左から右にひとつづつトラバース (traverse) します。 URL に PATH_INFO がない場合、publisher ハンドラは ‘index’ をデフォルト値に使います。パスの最 後の要素がモジュール内のオブジェクト名で、かつその前にある要素がディレクトリ名の場合 (モジュール 名が指定されていない場合)、モジュール名はデフォルト値の ‘index’ になります。 以下のような場合にはトラバースを停止して HTTP_NOT_FOUND を返します: • トラバースしたオブジェクトの名前がアンダースコア (‘_’) で始まっている場合。 逆に、Web からア クセスさせたくないオブジェクトを保護するにはアンダースコアを使ってください。 • モジュールに到達した場合。セキュリティ上の理由から、モジュールはパブリッシュの対象にできま せん。 パス上にオブジェクトが見つからなかった場合、クライアントに HTTP_NOT_FOUND を返します。 例えば、以下のような設定があるとしましょう: DocumentRoot /some/dir <Directory /some/dir> SetHandler mod_python PythonHandler mod_python.publisher </Directory> そして、以下のようなファイル ‘/some/dir/index.py’ があったとします: def index(req): return "We are in index()" def hello(req): return "We are in hello()" URI と結果は次のようになります: http://www.somehost/index/index は ‘We are in index()’ になります。 http://www.somehost/index/ は ‘We are in index()’ になります。 http://www.somehost/index/hello は ‘We are in hello()’ になります。 http://www.somehost/hello は ‘We are in hello()’ になります。 http://www.somehost/spam は ‘404 Not Found’ になります。 引数のマッチングと呼び出し publisher ハンドラがパブリッシュ対象のオブジェクトを見つけると、オブジェクトが呼び出し可能オブジェ クトであってクラスでない場合にはオブジェクトの受け取る引数のリストを取得します。次にこのリスト を POST や GET でクライアントから提出された HTML の各フォームデータの名前と比較します。引数と名 前の一致するフィールドの値は、文字列として呼び出し可能オブジェクトの該当する引数に渡します。名 前の一致しないフィールドは暗黙のまま捨てます。ただし、対象のオブジェクトが **kwargs 形式の引数 を受け取る場合には、名前の一致しなかったフィールドを **kwargs 引数で渡します。 58 パブリッシュ対象のオブジェクトが呼び出し可能オブジェクトの場合やクラスの場合、その文字列表現 をクライアントに返します。 認証 publisher ハンドラはモジュールや関数へのアクセスを制御する簡単な手段を備えています。 トラバースの各ステップで、publisher ハンドラは__auth__ や__access__ 、そして__auth_realm__ といった属性を (列挙した順番で) 探します。 __auth__ が定義されていて、かつ呼び出し可能オブジェクトの場合、Request オブジェクト、ユーザ 名の入った文字列、そしてパスワードの入った文字列、の三つの引数を使って呼び出します。__auth__の 戻り値が偽の場合、HTTP_UNAUTHORIZED をクライアントに返します (通常は、クライアント側でパス ワード入力ダイアログが表示されます)。 __auth__ が辞書の場合、ユーザ名とキー、そしてパスワードとキーに関連づけられた値とが一致する か調べます。キーとパスワードの両方が一致しなかった場合、HTTP_UNAUTHORIZED を返します。この 処理ではパスワードをソースコードに平文で保存せねばならないため、あまり安全ではありません。 __auth__ は定数にもできます。この場合、値が偽 (None, 0, "" など) ならば HTTP_UNAUTHORIZED を返します。 __auth_realm__ 文字列がある場合、認証レルム (Authorization Realm) としてクライアントに送信さ れます (通常、パスワード入力ダイアログボックスの上にあるテキストになります)。 __access__ が存在し、かつ呼び出し可能オブジェクトの場合、Request オブジェクトとユーザ名か らなる二つの引数を渡して呼び出します。__access__ の戻り値が偽ならば、HTTP_FORBIDDEN をクラ イアントに返します。 __access__ がリストの場合、ユーザ名がリストのいずれかの要素に一致するか調べます。ユーザ名が リスト中にない場合、HTTP_FORBIDDEN を返します。 __auth__ と同様、__access__ も定数にできます。 下の例では、ユーザが ‘eggs’ でパスワードが ‘spam’ の場合にのみ hello 関数にアクセスできます: __auth_realm__ = "Members only" def __auth__(req, user, passwd): if user == "eggs" and passwd == "spam" or \ user == "joe" and passwd == "eoj": return 1 else: return 0 def __access__(req, user): if user == "eggs": return 1 else: return 0 def hello(req): return "hello" 下の例も同じような機能を実現しますが、別のテクニックを使っています: 59 __auth_realm__ = "Members only" __auth__ = {"eggs":"spam", "joe":"eoj"} __access__ = ["eggs"] def hello(req): return "hello" 関数には属性値を代入できないため、特定の関数を保護するには関数の中で__auth__ や __access__ を関数として定義します: def sensitive(req): def __auth__(req, user, password): if user == ’spam’ and password == ’eggs’: # let them in return 1 else: # no access return 0 # something involving sensitive information return ’sensitive information‘ このテクニックは__auth__ や__access__ を定数にしても通用しますが、辞書やリストの場合には うまくいきません。 __auth__ や __access__ のメカニズムは標準の PythonAuthenHandler から独立しています。例えば、 認証にはハンドラを使い、次に認証済みのユーザが特定の関数にアクセスできるかを__access__ リスト で検証できます。 注意: mod_python が__auth__ にアクセスできるようにするには、まず__auth__の入ったモジュー ルを import せねばなりません。この場合、たとえ__auth__ を偽に設定していても import 操作中にモ ジュールレベルのコードが実行されてしまいます。モジュールを完全にアクセス制限したければ、Apache の mod_auth や mod_python の PythonAuthenHandler ハンドラといった他の認証メカニズムを使ってく ださい。 6.1.3 フォームデータ 引数の一致を行う過程で、publisher ハンドラは FieldStorage クラスのインスタンスを生成します。このイ ンスタンスへの参照は Request オブジェクトの form 属性に入っています。 FieldStorage はリクエスト中で一度しか初期化できないため、publisher ハンドラを使う際には自分 で FieldStorage を初期化しようと試みてはなりません。代わりに Request.form を使ってください。 6.2 PSP ハンドラ PSP ハンドラはドキュメントを mod_python.psp モジュールの PSP クラスで処理するハンドラです。 PSP ハンドラを使いたければ、httpd の設定に以下の行を追加するだけです: 60 AddHandler mod_python .psp PythonHandler mod_python.psp PSP の詳しい文法は 4.9 節を参照してください。 サーバの設定で PythonDebug が On になっている場合、(‘_’) を URI の末尾に追加すると、元の PSP コードと psp モジュールの生成した Python コードを横に並べた綺麗なリストを表示できます。デバッグ に便利な機能です。 注意: 運用環境でデバッグオプションを残しておくと、遠隔のユーザが PSP ページのソースコードを表 示できてしまいます。 6.3 CGI ハンドラ CGI ハンドラは、mod_python 下で CGI 環境をエミュレートするためのハンドラです。 この環境は真の CGI 環境ではなく、Python レベルでエミュレートされたものだということに注意して ください。stdin および stdout は sys.stdin と sys.stdout で置き換わり、環境変数は辞書に置き 換わります。このことから、CGI ハンドラの環境から os.system などで呼び出された外部プログラムは Python プログラム側では利用できる環境変数を見られないばかりか、‘真の’ CGI 環境ではできるはずの標 準入出力を使った結果の読み書きも行えません。 このハンドラは CGI の古いコードから移行するための踏み石として提供されています。mod_python を使う方法として、このハンドラに長く腰を落ち着けるのはお勧めしません。というのは、CGI 環境はス レッド内での実行を想定していないため (例えば、CGI の実行には現在のディレクトリの変更が必要です が、これは本来スレッドセーフではありません。cgihandler はこの問題を解決するためにスレッドをロック して、マルチスレッドサーバであっても一度に一つのリクエストしか処理できないようにしてしまいます)、 mod_python を使う数多くの利点を最初からだめにしてしまうようにしか実装できなくなるからです。 CGI ハンドラを使いたければ、‘.htaccess’ ファイルに: SetHandler mod_python PythonHandler mod_python.cgihandler のように書くだけです。 バージョン 2.7 からは、 cgihandler は間接的に import されたモジュールも正しくリロードできるように なりました。これは CGI スクリプトの呼び出し前にロード済みのモジュールのリスト (sys.modules) を保存 しておき、CGI スクリプトの実行後のリストと比較することで実現しています。 新たに import されたモ ジュールは (__file__ 属性が標準 Python ライブラリの起き場所を指しているものを除いて) sys.modules か ら削除され、Python に次の CGI スクリプトが再度そのモジュールをロードできるようにさせています。 上記の動作を望まないのなら、‘cgihandler.py’ ファイルを編集して、### で区切られているコードをコメ ントアウトしてください。 テストの結果、 cgihandler は多数のファイルアップロードを処理する際に何らかのメモリリークを起こ すことが分かっています。原因についてはまだよく分かっていません。この問題を回避するには Apache の MaxRequestsPerChild 設定をゼロでない値にしてください。 61 付録 A バージョン 2.x からの変更 • mod_python 3.0 はもはや Apache 1.3 では動作せず、Apache 2.0 だけをサポートするようになりま した。 • Python 2.2.1 よりも前のバージョンでは動作しなくなりました。 • Apache フィルタをサポートするようになりました。 • Apache 接続ハンドラをサポートするようになりました。 • リクエストオブジェクトが internal_redirect() をサポートするようになりました。 • 接続オブジェクトに read(), readline() および write() が追加されました。 • サーバオブジェクトに get_config() が追加されました。 • httpdapi ハンドラが撤廃されました。 • Zpublisher ハンドラが撤廃されました。 • ユーザ名は req.connection.user ではなく req.user になりました。 63 付録 B 日本語訳について この和訳は,菊地時夫氏、吉田勝彦氏の手で翻訳された「Mod_python Manual Release 3.1.3」をもとに、 Python ドキュメント和訳プロジェクトが改訳を行ったものです. 作業者 (敬称略、ローマ字表記順) • Keisuke URAGO • Yasushi MASUDA 65 索引 Symbols C ./configure, 4 --with-apxs, 4 --with-python, 4 _apache module, 20 mod_python compiling, 3 --with-apxs ./configure, 4 --with-python ./configure, 4 canonical_filename (request の属性), 29 CGI, 61 Changes from version 2.x, 63 chunked (request の属性), 27 cleanup() (BaseSession のメソッド), 41 clength (request の属性), 27 close() (filter のメソッド), 31 closed (filter の属性), 32 compiling mod_python, 3 config_tree() (apache モジュール), 21 connection handler, 19 object, 30 オブジェクト, 30 connection (request の属性), 26 content_encoding (request の属性), 28 content_type (request の属性), 28 Cookie Cookie のクラス, 37 拡張 module, 36 created() (BaseSession のメソッド), 41 A aborted (connection の属性), 30 add() (table のメソッド), 23 add_common_vars() (request のメソッド), 23 add_cookie() (Cookie モジュール), 38 add_handler() (request のメソッド), 23 allow_methods() apache モジュール, 21 request のメソッド, 23 allowed (request の属性), 27 allowed_methods (request の属性), 27 allowed_xmethods (request の属性), 27 ap_auth_type (request の属性), 29 apache (拡張 module), 20 apply_data() (PSPInstance のメソッド), 45 apxs, 4 args (request の属性), 29 assbackwards (request の属性), 26 AUTH_TYPE, 29 D DbmSession (Session のクラス), 42 defn_line_number (server の属性), 32 defn_name (server の属性), 32 delete() (BaseSession のメソッド), 41 disable() (filter のメソッド), 31 display_code() (PSP のメソッド), 45 disposition (Field の属性), 35 disposition_options (Field の属性), 35 document_root() (request のメソッド), 24 double_reverse (connection の属性), 30 B base_server (connection の属性), 30 BaseSession (Session のクラス), 40 bytes_sent (request の属性), 27 67 E H environment variables AUTH_TYPE, 29 PATH_INFO, 29 PATH, 4 QUERY_ARGS, 29 REMOTE_ADDR, 30 REMOTE_HOST, 30 REMOTE_IDENT, 30 REMOTE_USER, 28 REQUEST_METHOD, 27 SERVER_NAME, 32 SERVER_PORT, 32 SERVER_PROTOCOL, 27 eos_sent (request の属性), 29 err_headers_out (request の属性), 28 error_fname (server の属性), 32 expecting_100 (request の属性), 28 handler connection, 19 filter, 18 request, 15 handler filter の属性, 32 request の属性, 28 header_only (request の属性), 27 headers_in (request の属性), 28 headers_out (request の属性), 28 hostname (request の属性), 27 httpdapi, 63 Httpdapy, 63 I id() (BaseSession のメソッド), 41 id (connection の属性), 31 import_module() (apache モジュール), 20 init_lock() (BaseSession のメソッド), 41 install_dso make targets, 5 install_py_lib make targets, 5 installation UNIX, 3 internal_redirect() (request のメソッド), 24 interpreter (request の属性), 28 invalidate() (BaseSession のメソッド), 41 is_input (filter の属性), 32 is_new() (BaseSession のメソッド), 40 is_virtual (server の属性), 33 F Field (util のクラス), 35 FieldStorage (util のクラス), 34 file (Field の属性), 35 filename Field の属性, 35 request の属性, 29 filter handler, 18 object, 31 オブジェクト, 31 finfo (request の属性), 29 flush() filter のメソッド, 31 request のメソッド, 26 K G keep_alive (server の属性), 33 keep_alive_max (server の属性), 33 keep_alive_timeout (server の属性), 33 keepalive (connection の属性), 30 keepalives (connection の属性), 31 get_basic_auth_pw() (request のメソッド), 24 get_config() request のメソッド, 24 server のメソッド, 32 get_cookies() (Cookie モジュール), 38 get_options() (request のメソッド), 24 get_remote_host() (request のメソッド), 24 getfirst() (FieldStorage のメソッド), 34 getlist() (FieldStorage のメソッド), 34 L last_accessed() (BaseSession のメソッド), 41 libpython.a, 4 limit_req_fields (server の属性), 33 limit_req_fieldsize (server の属性), 33 68 limit_req_line (server の属性), 33 list (FieldStorage の属性), 34 load() (BaseSession のメソッド), 41 local_addr (connection の属性), 30 local_host (connection の属性), 31 local_ip (connection の属性), 31 lock() (BaseSession のメソッド), 41 log_error() apache モジュール, 20 table のメソッド, 24 loglevel (server の属性), 33 request, 16 server, 32 table, 22 オブジェクト connection, 30 filter, 31 server, 32 table, 22 order phase, 48 P M parse() Cookie のメソッド, 37 psp モジュール, 46 SignedCookie のメソッド, 37 parse_qs() (util モジュール), 35 parse_qsl() (util モジュール), 36 parsed_uri (request の属性), 29 parsestring() (psp モジュール), 46 pass_on() (filter のメソッド), 31 PATH, 4 path (server の属性), 33 PATH_INFO, 29 path_info (request の属性), 29 pathlen (server の属性), 33 phase order, 48 phase (request の属性), 28 port (server の属性), 32 prev (request の属性), 26 proto_num (request の属性), 27 protocol (request の属性), 27 proxyreq (request の属性), 26 PSP, 60 PSP (psp のクラス), 44 psp (拡張 module), 42 PSPInstance (psp のクラス), 45 Python*Handler 構文, 47 PythonAccessHandler, 49 PythonAuthenHandler, 49 PythonAuthzHandler, 50 PythonAutoReload, 55 PythonCleanupHandler, 51 PythonConnectionHandler, 52 PythonDebug, 52 PythonEnablePdb, 52 mailing list mod_python, 3 main (request の属性), 26 make targets install_dso, 5 install_py_lib, 5 make_table() (apache モジュール), 21 MarshalCookie (Cookie のクラス), 38 MemorySession (Session のクラス), 42 method (request の属性), 27 method_number (request の属性), 27 mod_python mailing list, 3 mod_python.so, 5 module _apache, 20 mpm_query() (apache モジュール), 21 mtime (request の属性), 27 N name Field の属性, 35 filter の属性, 32 next (request の属性), 26 no_cache (request の属性), 29 no_local_copy (request の属性), 29 notes connection の属性, 31 request の属性, 28 O object connection, 30 filter, 31 69 PythonFixupHandler, 50 PythonHandler, 50 PythonHandlerModule, 54 PythonHeaderParserHandler, 48 PythonImport, 53 PythonInitHandler, 48 PythonInputFilter, 51 PythonInterpPerDirectory, 53 PythonInterpreter, 54 PythonLogHandler, 51 PythonOptimize, 55 PythonOption, 55 PythonOutputFilter, 51 PythonPath, 55 PythonPostReadRequestHandler, 48 PythonPythonInterpPerDirective, 53 PythonTransHandler, 48 PythonTypeHandler, 50 REMOTE_IDENT, 30 remote_ip (connection の属性), 30 remote_logname (connection の属性), 30 REMOTE_USER, 28 req, 16 req (filter の属性), 32 request, 23 handler, 15 object, 16 REQUEST_METHOD, 27 request_time (request の属性), 27 requires() (request のメソッド), 25 RFC RFC 1867, 35 RFC 2109, 36 RFC 2964, 36 RFC 2965, 36 run() (PSP のメソッド), 44 Q S QUERY_ARGS, 29 save() (BaseSession のメソッド), 41 sendfile() (request のメソッド), 26 sent_bodyct (request の属性), 27 server object, 32 オブジェクト, 32 server (request の属性), 26 server_admin (server の属性), 32 server_hostname (server の属性), 32 SERVER_NAME, 32 SERVER_PORT, 32 SERVER_PROTOCOL, 27 server_root() (apache モジュール), 21 Session() (Session モジュール), 40 Session (拡張 module), 39 set_content_length() (request のメソッド), 26 set_error_page() (PSPInstance のメソッド), 45 set_timeout() (BaseSession のメソッド), 41 SignedCookie (Cookie のクラス), 37 status (request の属性), 27 status_line (request の属性), 27 subprocess_env (request の属性), 28 R range (request の属性), 27 read() connection のメソッド, 30 filter のメソッド, 31 request のメソッド, 25 read_body (request の属性), 28 read_chunked (request の属性), 28 read_length (request の属性), 28 readline() connection のメソッド, 30 filter のメソッド, 31 request のメソッド, 25 readlines() (request のメソッド), 25 redirect() PSPInstance のメソッド, 45 util モジュール, 36 register_cleanup() request のメソッド, 25 server のメソッド, 32 remaining (request の属性), 27 REMOTE_ADDR, 30 remote_addr (connection の属性), 30 REMOTE_HOST, 30 remote_host (connection の属性), 30 T table, 22 70 object, 22 オブジェクト, 22 table (apache のクラス), 22 the_request (request の属性), 26 timeout() (BaseSession のメソッド), 41 timeout (server の属性), 33 type (Field の属性), 35 type_options (Field の属性), 35 U UNIX installation, 3 unlock() (BaseSession のメソッド), 41 unparsed_uri (request の属性), 29 uri (request の属性), 29 used_path_info (request の属性), 29 user (request の属性), 28 util (拡張 module), 33 V value (Field の属性), 35 version 2.x Changes from, 63 vlist_validator (request の属性), 28 W write() connection のメソッド, 30 filter のメソッド, 31 request のメソッド, 26 Z ZPublisher, 63 71
© Copyright 2024 Paperzz