目次

前のトピックへ

20.22. Cookie — HTTPの状態管理

次のトピックへ

20.24. SimpleXMLRPCServer — 基本的なXML-RPCサーバー

このページ

20.23. xmlrpclib — XML-RPC クライアントアクセス

ノート

xmlrpclib モジュールは、Python 3では xmlrpc.client にリネームされました。 2to3 ツールは、自動的にソースコードのimportをPython 3用に修正します。

バージョン 2.2 で追加.

XML-RPCはXMLを利用した遠隔手続き呼び出し(Remote Procedure Call)の一種で、HTTPをトランスポートとして使用します。XML- RPCでは、クライアントはリモートサーバ(URIで指定されたサーバ)上のメソッドをパラメータを指定して呼び出し、構造化されたデータを取得します。このモジュールは、XML-RPCクライアントの開発をサポートしており、Pythonオブジェクトに適合する転送用XMLの変換の全てを行います。

class xmlrpclib.ServerProxy(uri[, transport[, encoding[, verbose[, allow_none[, use_datetime]]]]])

ServerProxy は、リモートのXML-RPCサーバとの通信を管理するオブジェクトです。最初のパラメータはURI(Uniform Resource Indicator)で、通常はサーバのURLを指定します。2番目のパラメータにはトランスポート・ファクトリを指定する事ができます。トランスポート・ファクトリを省略した場合、URLが https: ならモジュール内部の SafeTransport インスタンスを使用し、それ以外の場合にはモジュール内部の Transport インスタンスを使用します。オプションの 3 番目の引数はエンコード方法で、デフォルトでは UTF-8 です。オプションの 4 番目の引数はデバッグフラグです。 allow_none が真の場合、Python の定数 None は XML に翻訳されます; デフォルトの動作は None に対して TypeError を送出します。この仕様は XML-RPC 仕様でよく用いられている拡張ですが、全てのクライアントやサーバでサポートされているわけではありません; 詳細記述については http://ontosys.com/xml-rpc/extensions.html を参照してください。 use_datetime フラグは datetime.datetime のオブジェクトとして日付/時刻を表現する時に使用し、デフォルトでは false に設定されています。呼び出しに datetime.datetime のオブジェクトを渡すことができます。

HTTP及びHTTPS通信の両方で、 http://user:pass@host:port/path のようなHTTP基本認証のための拡張URL構文をサポートしています。 user:pass はbase64でエンコードしてHTTPの’Authorization ‘ヘッダとなり、XML-RPCメソッド呼び出し時に接続処理の一部としてリモートサーバに送信されます。リモートサーバが基本認証を要求する場合のみ、この機能を利用する必要があります。

生成されるインスタンスはリモートサーバへのプロクシオブジェクトで、RPC呼び出しを行う為のメソッドを持ちます。リモートサーバがイントロスペクション APIをサポートしている場合は、リモートサーバのサポートするメソッドを検索 (サービス検索)やサーバのメタデータの取得なども行えます。

ServerProxy インスタンスのメソッドは引数としてPythonの基礎型とオブジェクトを受け取り、戻り値としてPythonの基礎型かオブジェクトを返します。以下の型をXMLに変換(XMLを通じてマーシャルする)する事ができます (特別な指定がない限り、逆変換でも同じ型として変換されます):

名前 意味
boolean 定数 TrueFalse
整数 そのまま
浮動小数点 そのまま
文字列 そのまま
配列 変換可能な要素を含むPythonシーケンス。戻り値はリスト。
構造体 Pythonの辞書。キーは文字列のみ。全ての値は変換可能でなくてはならない。ユーザー定義型を渡すこともできます。 __dict__ の属性のみ転送されます。
日付 エポックからの経過秒数(DateTime クラスのインスタンスとして渡す) もしくは、 datetime.datetime のインスタンス
バイナリ Binary ラッパクラスのインスタンス

上記のXML-RPCでサポートする全データ型を使用することができます。メソッド呼び出し時、XML- RPCサーバエラーが発生すると Fault インスタンスを送出し、HTTP/HTTPSトランスポート層でエラーが発生した場合には ProtocolError を送出します。 Error をベースとする FaultProtocolError の両方が発生します。 Python 2.2以降では組み込み型のサブクラスを作成する事ができますが、現在のところxmlrpclibではそのようなサブクラスのインスタンスをマーシャルすることはできません。

文字列を渡す場合、 <, >, & などのXMLで特殊な意味を持つ文字は自動的にエスケープされます。しかし、ASCII値0〜31の制御文字(もちろん、タブ’TAB’,改行’LF’,リターン’CR’は除く)などのXMLで使用することのできない文字を使用することはできず、使用するとそのXML-RPCリクエストはwell-formedなXMLとはなりません。そのような文字列を渡す必要がある場合は、後述の Binary ラッパクラスを使用してください。

Server は、上位互換性の為に ServerProxy の別名として残されています。新しいコードでは ServerProxy を使用してください。

バージョン 2.5 で変更: use_datetime フラグが追加されました

バージョン 2.6 で変更: ニュースタイルクラス(new-style class)も、 __dict__ 属性を持っていて、特別な方法でマーシャルされている親クラスを 持っていなければ、渡すことができます。

参考

XML-RPC HOWTO
週種類のプログラミング言語で記述された XML-RPCの操作とクライアントソフトウェアの素晴らしい説明が掲載されています。 XML- RPCクライアントの開発者が知っておくべきことがほとんど全て記載されています。
XML-RPC-Hacks page
イントロスペクションとマルチコールをサポートしているオープンソースの拡張ライブラリについて説明しています。
XML-RPC Introspection
インストロペクションをサポートする、 XML-RPC プロトコルの拡張を解説しています。
XML-RPC Specification
公式の仕様
Unofficial XML-RPC Errata
Fredrik Lundh による “unofficial errata, intended to clarify certain details in the XML-RPC specification, as well as hint at ‘best practices’ to use when designing your own XML-RPC implementations.”

20.23.1. ServerProxy オブジェクト

ServerProxy インスタンスの各メソッドはそれぞれXML-RPCサーバの遠隔手続き呼び出しに対応しており、メソッドが呼び出されると名前と引数をシグネチャとしてRPCを実行します(同じ名前のメソッドでも、異なる引数シグネチャによってオーバロードされます)。RPC実行後、変換された値を返すか、または Fault オブジェクトもしくは ProtocolError オブジェクトでエラーを通知します。

予約メンバ system から、XMLイントロスペクションAPIの一般的なメソッドを利用する事ができます。

ServerProxy.system.listMethods()

XML-RPCサーバがサポートするメソッド名(system以外)を格納する文字列のリストを返します。

ServerProxy.system.methodSignature(name)

XML-RPCサーバで実装されているメソッドの名前を指定し、利用可能なシグネチャの配列を取得します。シグネチャは型のリストで、先頭の型は戻り値の型を示し、以降はパラメータの型を示します。

XML-RPCでは複数のシグネチャ(オーバロード)を使用することができるので、単独のシグネチャではなく、シグネチャのリストを返します。

シグネチャは、メソッドが使用する最上位のパラメータにのみ適用されます。例えばあるメソッドのパラメータが構造体の配列で戻り値が文字列の場合、シグネチャは単に”文字列, 配列” となります。パラメータが三つの整数で戻り値が文字列の場合は”文字列, 整数, 整数, 整数”となります。

メソッドにシグネチャが定義されていない場合、配列以外の値が返ります。 Pythonでは、この値はlist以外の値となります。

ServerProxy.system.methodHelp(name)

XML-RPCサーバで実装されているメソッドの名前を指定し、そのメソッドを解説する文書文字列を取得します。文書文字列を取得できない場合は空文字列を返します。文書文字列にはHTMLマークアップが含まれます

20.23.2. Boolean オブジェクト

このクラスは全てのPythonの値で初期化することができ、生成されるインスタンスは指定した値の真偽値によってのみ決まります。Booleanという名前から想像される通りに各種のPython演算子を実装しており、 __cmp__(), __repr__(), __int__(), __nonzero__() で定義される演算子を使用することができます。

以下のメソッドは、主に内部的にアンマーシャル時に使用されます:

Boolean.encode(out)

出力ストリームオブジェクト out に、XML-RPCエンコーディングのBoolean値を出力します。

動作する例です。サーバー側:

import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer

def is_even(n):
    return n%2 == 0

server = SimpleXMLRPCServer(("localhost", 8000))
print "Listening on port 8000..."
server.register_function(is_even, "is_even")
server.serve_forever()

上記のサーバーに対するクライアント側:

import xmlrpclib

proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
print "3 is even: %s" % str(proxy.is_even(3))
print "100 is even: %s" % str(proxy.is_even(100))

20.23.3. DateTime オブジェクト

このクラスは、エポックからの秒数、タプルで表現された時刻、ISO 8601形式の時間/日付文字列、 datetime.datetime, のインスタンスのいずれかで初期化することができます。このクラスには以下のメソッドがあり、主にコードをマーシャル/アンマーシャルするための内部処理を行います。

DateTime.decode(string)

文字列をインスタンスの新しい時間を示す値として指定します。

DateTime.encode(out)

出力ストリームオブジェクト out に、XML-RPCエンコーディングの DateTime 値を出力します。

また、 __cmp__()__repr__() で定義される演算子を使用することができます。

20.23.4. Binary オブジェクト

このクラスは、文字列(NULを含む)で初期化することができます。 Binary の内容は、属性で参照します。

Binary.data

Binary インスタンスがカプセル化しているバイナリデータ。このデータは8bitクリーンです。

以下のメソッドは、主に内部的にマーシャル/アンマーシャル時に使用されます:

Binary.decode(string)

指定されたbase64文字列をデコードし、インスタンスのデータとします。

Binary.encode(out)

バイナリ値をbase64でエンコードし、出力ストリームオブジェクト out に出力します。

エンコードされたデータは、 RFC 2045 section 6.8 にある通り、76文字ごとに改行されます。これは、XMC-RPC仕様が作成された時のデ・ファクト・スタンダードのbase64です。

また、 __cmp__() で定義される演算子を使用することができます。

バイナリオブジェクトの使用例です。 XML-RPCごしに画像を転送します。

from SimpleXMLRPCServer import SimpleXMLRPCServer
import xmlrpclib

def python_logo():
     with open("python_logo.jpg", "rb") as handle:
         return xmlrpclib.Binary(handle.read())

server = SimpleXMLRPCServer(("localhost", 8000))
print "Listening on port 8000..."
server.register_function(python_logo, 'python_logo')

server.serve_forever()

クライアント側は画像を取得して、ファイルに保存します。

import xmlrpclib

proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
with open("fetched_python_logo.jpg", "wb") as handle:
    handle.write(proxy.python_logo().data)

20.23.5. Fault オブジェクト

Fault オブジェクトは、XML-RPCのfaultタグの内容をカプセル化しており、以下のメンバを持ちます:

Fault.faultCode

失敗のタイプを示す文字列。

Fault.faultString

失敗の診断メッセージを含む文字列。

以下のサンプルでは、複素数型のオブジェクトを返そうとして、故意に Fault を起こしています。

from SimpleXMLRPCServer import SimpleXMLRPCServer

# A marshalling error is going to occur because we're returning a
# complex number
def add(x,y):
    return x+y+0j

server = SimpleXMLRPCServer(("localhost", 8000))
print "Listening on port 8000..."
server.register_function(add, 'add')

server.serve_forever()

上記のサーバーに対するクライアント側のコード:

import xmlrpclib

proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
try:
    proxy.add(2, 5)
except xmlrpclib.Fault, err:
    print "A fault occurred"
    print "Fault code: %d" % err.faultCode
    print "Fault string: %s" % err.faultString

20.23.6. ProtocolError オブジェクト

ProtocolError オブジェクトはトランスポート層で発生したエラー(URI で指定したサーバが見つからなかった場合に発生する404 ‘not found’など)の内容を示し、以下のメンバを持ちます:

ProtocolError.url

エラーの原因となったURIまたはURL。

ProtocolError.errcode

エラーコード。

ProtocolError.errmsg

エラーメッセージまたは診断文字列。

ProtocolError.headers

エラーの原因となったHTTP/HTTPSリクエストを含む文字列。

次の例では、XMLRPC サーバを指していない URI を利用して、故意に ProtocolError を発生させています。

import xmlrpclib

# create a ServerProxy with an URI that doesn't respond to XMLRPC requests
proxy = xmlrpclib.ServerProxy("http://www.google.com/")

try:
    proxy.some_method()
except xmlrpclib.ProtocolError, err:
    print "A protocol error occurred"
    print "URL: %s" % err.url
    print "HTTP/HTTPS headers: %s" % err.headers
    print "Error code: %d" % err.errcode
    print "Error message: %s" % err.errmsg

20.23.7. MultiCall オブジェクト

バージョン 2.4 で追加.

遠隔のサーバに対する複数の呼び出しをひとつのリクエストにカプセル化する方法は、http://www.xmlrpc.com/discuss/msgReader%241208 で示されています。

class xmlrpclib.MultiCall(server)

巨大な (boxcar) メソッド呼び出しに使えるオブジェクトを作成します。 server には最終的に呼び出しを行う対象を指定します。作成した MultiCall オブジェクトを使って呼び出しを行うと、即座に None を返し、呼び出したい手続き名とパラメタに保存するだけに留まります。オブジェクト自体を呼び出すと、それまでに保存しておいたすべての呼び出しを単一の system.multicall リクエストの形で伝送します。呼び出し結果はジェネレータ(generator)になります。このジェネレータにわたってイテレーションを行うと、個々の呼び出し結果を返します。

以下にこのクラスの使い方を示します。

このクラスの使用例です。サーバー側のコード:

from SimpleXMLRPCServer import SimpleXMLRPCServer

def add(x,y):
    return x+y

def subtract(x, y):
    return x-y

def multiply(x, y):
    return x*y

def divide(x, y):
    return x/y

# A simple server with simple arithmetic functions
server = SimpleXMLRPCServer(("localhost", 8000))
print "Listening on port 8000..."
server.register_multicall_functions()
server.register_function(add, 'add')
server.register_function(subtract, 'subtract')
server.register_function(multiply, 'multiply')
server.register_function(divide, 'divide')
server.serve_forever()

このサーバーに対する、クライアント側のコード:

import xmlrpclib

proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
multicall = xmlrpclib.MultiCall(proxy)
multicall.add(7,3)
multicall.subtract(7,3)
multicall.multiply(7,3)
multicall.divide(7,3)
result = multicall()

print "7+3=%d, 7-3=%d, 7*3=%d, 7/3=%d" % tuple(result)

20.23.8. 補助関数

xmlrpclib.boolean(value)

Pythonの値を、XML-RPCのBoolean定数 True または False に変換します。

xmlrpclib.dumps(params[, methodname[, methodresponse[, encoding[, allow_none]]]])

params を XML-RPC リクエストの形式に変換します。 methodresponse が真の場合、XML-RPC レスポンスの形式に変換します。 params に指定できるのは、引数からなるタプルか Fault 例外クラスのインスタンスです。 methodresponse が真の場合、単一の値だけを返します。従って、 params の長さも 1 でなければなりません。 encoding を指定した場合、生成される XML のエンコード方式になります。デフォルトは UTF-8 です。 Python の None は標準の XML-RPC には利用できません。 None を使えるようにするには、 allow_none を真にして、拡張機能つきにしてください。

xmlrpclib.loads(data[, use_datetime])

XML-RPC リクエストまたはレスポンスを (params, methodname) の形式をとる Python オブジェクトにします。 params は引数のタプルです。 methodname は文字列で、パケット中にメソッド名がない場合には None になります。例外条件を示す XML-RPC パケットの場合には、 Fault 例外を送出します。 use_datetime フラグは datetime.datetime のオブジェクトとして日付/時刻を表現する時に使用し、デフォルトでは false に設定されています。

バージョン 2.5 で変更: use_datetime フラグを追加.

20.23.9. クライアントのサンプル

# simple test program (from the XML-RPC specification)
from xmlrpclib import ServerProxy, Error

# server = ServerProxy("http://localhost:8000") # local server
server = ServerProxy("http://betty.userland.com")

print server

try:
    print server.examples.getStateName(41)
except Error, v:
    print "ERROR", v

XML-RPCサーバにプロキシを経由して接続する場合、カスタムトランスポートを定義する必要があります。以下に例を示します:

import xmlrpclib, httplib

class ProxiedTransport(xmlrpclib.Transport):
    def set_proxy(self, proxy):
        self.proxy = proxy
    def make_connection(self, host):
        self.realhost = host
        h = httplib.HTTP(self.proxy)
        return h
    def send_request(self, connection, handler, request_body):
        connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))
    def send_host(self, connection, host):
        connection.putheader('Host', self.realhost)

p = ProxiedTransport()
p.set_proxy('proxy-server:8080')
server = xmlrpclib.Server('http://time.xmlrpc.com/RPC2', transport=p)
print server.currentTime.getCurrentTime()

20.23.10. クライアントとサーバーの利用例

SimpleXMLRPCServer の例 を参照してください。