SimpleXMLRPCServerはpathが”/”か”/RPC2”じゃないと404を返す

Apacheでできているリバースプロキシをlighttpdに移行する設定を書いていて困った。

Apacheと同じように設定してみると、書き換え後のURLでバックエンドにリクエストしていないように見える。

調べてみると、lighttpdのmod_rewriteは$HTTP[“url”]の内側では無効になるということらしい。

http://trac.lighttpd.net/trac/wiki/Docs%3AModRewrite

NOTE: url rewriting does not work within a $HTTP[“url”] conditional. http://forum.lighttpd.net/topic/1092#3028

でもURLでしか区別のつかないリクエストをどうしてもバックエンドで受けたい。いまさらユーザに見えるURLを変えることができないし。

バックエンド側ではPythonのSimpleXMLRPCServerで作ったもの。ソースを見てもどこにもURLをいじるところはない。そういえば、URLは意識したこと無いなあ。

で、探して探して見つけた。

[ python-Bugs-1473048 ] SimpleXMLRPCServer responds to any path

SimpleXMLRPCRequestHandlerのソースを見ると、

class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
	"""Simple XML-RPC request handler class.
	Handles all HTTP POST requests and attempts to decode them as
	XML-RPC requests.
	"""

	# Class attribute listing the accessible path components;
	# paths not on this list will result in a 404 error.
	rpc_paths = ('/', '/RPC2')

	def is_rpc_path_valid(self):
		if self.rpc_paths:
			return self.path in self.rpc_paths
		else:
			# If .rpc_paths is empty, just assume all paths are legal
			return True

	def do_POST(self):
		"""Handles the HTTP POST request.
		  Attempts to interpret all HTTP POST requests as XML-RPC calls,
		  which are forwarded to the server's _dispatch method for handling.
		  """

		# Check that the path is legal
		if not self.is_rpc_path_valid():
			self.report_404()
			return

なるほど。最初からソース見ればよかった。

SimpleXMLRPCRequestHandlerを継承したクラスで、is_rpc_path_validを継承して解決。

なんだか、達成感。

urllib2.quote()ってアリなんだ

そりゃそうだよなあ、と気がついてみれば当たり前だけれど。

#urllib2.pyより抜粋
from urllib import (unwrap, unquote, splittype, splithost, quote,
addinfourl, splitport, splitgophertype, splitquery,
splitattr, ftpwrapper, noheaders, splituser, splitpasswd, splitvalue)
# support for FileHandler, proxies via environment variables
from urllib import localhost, url2pathname, getproxies

マニュアルにはかいてないから*1、urllib2を使っていても、いつも別途urllibをimportして、urllib.quote()と書いていた。

まあurllib2の無い環境でも動かそうとか考えると作法としてはいいんだろうけど、そもそもurllib2使っているんなら関係ないし。

標準モジュールもよく使うものはソースに目を通しておくのがいいんだな。勉強になるし。

*1http://docs.python.org/lib/module-urllib2.htmlの"The urllib2 module defines the following functions:"以下に無いから勝手に無いと思っていた。他にもたくさんこんな思い込みがありそう。

cx_Oracleが見つけるOracleクライアントライブラリを変えたい

C:\Python25\python.exeを起動すると、C:\oracle9i\bin\oci.dllがシステムで最初に見つかるけれど、cx_Oracleの想定するバージョン(10.2)と違うのでシンボルが見つからなくて例外が起きる。

Python 2.5を使うときは常に10.2のOracleクライアントを使う(が見つかる)ようにしたい。でも他のアプリケーションには影響がないようにしたい。

以下のファイルを作成することで解決。

#C:\Python25\Lib\site-packages\sitecustomize.py
import  os
ORA_PATH=Z:\instantclient_10_2
#環境変数のPATHを書き換える
os.environ['PATH'] = "%s%s" % (ORA_PATH, os.environ['PATH'])
#print os.environ['PATH']

ちなみに、今回初めて、Oracle Instant Clientを使ってみたけど、これは楽でいいね。Oracle8用のも欲しいがなあ。

月末はcalendarモジュール

先日のエントリにいただいたトラックバックが勉強になった。

id:bonlife:20080313:1205408129

dateutilは僕もちょっと見たんですが、ちょこっと使うには標準ライブラリでもないし重すぎるなあと思って、作ってしまいました。

それよりも、このdateutilの中でのcalendarモジュールの使い方の解説が勉強になりました。これはいいですね。

近いうちにcalendarモジュールを使って書き直そうと思います。

ありがとうございました。

3月14日追記

書き直しid:bonlifeさんに感謝。

urllib2でプロキシを参照しないようにする

urllib.urlopenはステータス404でも例外を発生してくれない。

urllib2.urlopenは404で例外を発生してくれるけれど、そのままだと環境変数のプロキシ設定を参照するようで、ちょっと困る場合があった。

というわけで、urllib2.urlopenでプロキシを設定|参照しないようにする方法を調べてみた。

Gist:227549

#!/usr/bin/env python
"""Python urllib2 proxy usage sample
"""
import urllib2

#今回はプロキシ設定を空にしておく
#proxies = {'http': 'http://www.example.com:3128/'}
proxies = {}

#プロキシハンドラーを作成して
handler = urllib2.ProxyHandler(proxies)

#プロキシハンドラーを指定してURL Openerを作成して
opener = urllib2.build_opener(handler)

#作成したURL Openerをインストールしてから
urllib2.install_opener(opener)

#普通にurlopenすると例外が発生してくれる
try:
    u = urllib2.urlopen('http://www.example.com/404NotFound.html')
    print u.read()
except IOError,error:
    print error

Pythonカテゴリ作ってみた。