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

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

バージョン 2.2 で追加.

**Source code:** Lib/xmlrpclib.py

======================================================================

XML-RPC is a Remote Procedure Call method that uses XML passed via
HTTP(S) as a transport.  With it, a client can call methods with
parameters on a remote server (the server is named by a URI) and get
back structured data.  This module supports writing XML-RPC client
code; it handles all the details of translating between conformable
Python objects and XML on the wire.

警告: "xmlrpclib" モジュールは悪意を持って構築されたデータに対して安
  全では ありません。信頼できないデータや認証されていないデータを解析
  する必要 があるばあいは XML の脆弱性 を参照してください。

バージョン 2.7.9 で変更: For HTTPS URIs, "xmlrpclib" now performs all
the necessary certificate and hostname checks by default.

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

   A "ServerProxy" instance is an object that manages communication
   with a remote XML-RPC server.  The required first argument is a URI
   (Uniform Resource Indicator), and will normally be the URL of the
   server.  The optional second argument is a transport factory
   instance; by default it is an internal "SafeTransport" instance for
   https: URLs and an internal HTTP "Transport" instance otherwise.
   The optional third argument is an encoding, by default UTF-8. The
   optional fourth argument is a debugging flag.

   The following parameters govern the use of the returned proxy
   instance. If *allow_none* is true,  the Python constant "None" will
   be translated into XML; the default behaviour is for "None" to
   raise a "TypeError". This is a commonly-used extension to the XML-
   RPC specification, but isn't supported by all clients and servers;
   see http://ontosys.com/xml-rpc/extensions.php for a description.
   The *use_datetime* flag can be used to cause date/time values to be
   presented as "datetime.datetime" objects; this is false by default.
   "datetime.datetime" objects may be passed to calls.

   Both the HTTP and HTTPS transports support the URL syntax extension
   for HTTP Basic Authentication: "http://user:pass@host:port/path".
   The "user:pass" portion will be base64-encoded as an HTTP
   'Authorization' header, and sent to the remote server as part of
   the connection process when invoking an XML-RPC method.  You only
   need to use this if the remote server requires a Basic
   Authentication user and password. If an HTTPS URL is provided,
   *context* may be "ssl.SSLContext" and configures the SSL settings
   of the underlying HTTPS connection.

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

   Types that are conformable (e.g. that can be marshalled through
   XML), include the following (and except where noted, they are
   unmarshalled as the same Python type):

   +------------------------+---------------------------------------------------------+
   | XML-RPC の型           | Python の型                                             |
   +========================+=========================================================+
   | "boolean"              | "bool"                                                  |
   +------------------------+---------------------------------------------------------+
   | "int" または "i4"      | "int" or "long" in range from -2147483648 to            |
   |                        | 2147483647.                                             |
   +------------------------+---------------------------------------------------------+
   | "double"               | "float"                                                 |
   +------------------------+---------------------------------------------------------+
   | "string"               | "str" or "unicode"                                      |
   +------------------------+---------------------------------------------------------+
   | "array"                | 適合する要素を持つ "list" または "tuple"。array は      |
   |                        | "list" として返 します。                                |
   +------------------------+---------------------------------------------------------+
   | "struct"               | "dict".  Keys must be strings, values may be any        |
   |                        | conformable type. Objects of user-defined classes can   |
   |                        | be passed in; only their "__dict__" attribute is        |
   |                        | transmitted.                                            |
   +------------------------+---------------------------------------------------------+
   | "dateTime.iso8601"     | "DateTime" or "datetime.datetime". Returned type        |
   |                        | depends on values of the *use_datetime* flags.          |
   +------------------------+---------------------------------------------------------+
   | "base64"               | "Binary"                                                |
   +------------------------+---------------------------------------------------------+
   | "nil"                  | "None" 定数。 *allow_none* が真の場合にのみ渡すことが出 |
   |                        | 来ます。                                                |
   +------------------------+---------------------------------------------------------+

   上記のXML-RPCでサポートする全データ型を使用することができます。メソ
   ッド呼び出し時、XML- RPCサーバエラーが発生すると "Fault" インスタン
   スを送出し、HTTP/HTTPSトランスポート層でエラーが発生した場合には
   "ProtocolError" を送出します。 "Error" をベースとする "Fault" と
   "ProtocolError" の両方が発生します。 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__* 属性を持っていて、特別な方法でマーシャルされている親クラ
   スを持っていなければ、渡すことができます。

   バージョン 2.7.9 で変更: *context* 引数が追加されました。

参考:

  XML-RPC HOWTO
     数種類のプログラミング言語で記述された XML-RPCの操作とクライアン
     トソフトウェアの素晴らしい説明が掲載されています。 XML- RPCクライ
     アントの開発者が知っておくべきことがほとんど全て記載されています
     。

  XML-RPC Introspection
     インストロペクションをサポートする、 XML-RPC プロトコルの拡張を解
     説しています。

  XML-RPC Specification
     公式の仕様

  XML-RPC 非公式正誤表
     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" オブジェクトでエラーを通
知します。

Servers that support the XML introspection API support some common
methods grouped under the reserved "system" attribute:

ServerProxy.system.listMethods()

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

ServerProxy.system.methodSignature(name)

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

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

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

   メソッドにシグネチャが定義されていない場合、配列以外の値が返ります
   。 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 オブジェクト
==============================

class xmlrpclib.DateTime

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

   decode(string)

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

   encode(out)

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

   It also supports certain of Python's built-in operators through
   "__cmp__()" and "__repr__()" methods.

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

   import datetime
   from SimpleXMLRPCServer import SimpleXMLRPCServer
   import xmlrpclib

   def today():
       today = datetime.datetime.today()
       return xmlrpclib.DateTime(today)

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

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

   import xmlrpclib
   import datetime

   proxy = xmlrpclib.ServerProxy("http://localhost:8000/")

   today = proxy.today()
   # convert the ISO8601 string to a datetime object
   converted = datetime.datetime.strptime(today.value, "%Y%m%dT%H:%M:%S")
   print "Today: %s" % converted.strftime("%d.%m.%Y, %H:%M")


20.23.4. Binary オブジェクト
============================

class xmlrpclib.Binary

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

   data

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

   "Binary" オブジェクトは以下のメソッドを持ち、主に内部的にマーシャル
   /アンマーシャル時に使用されます:

   decode(string)

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

   encode(out)

      Write the XML-RPC base 64 encoding of this binary item to the
      *out* stream object.

      The encoded data will have newlines every 76 characters as per
      RFC 2045 section 6.8, which was the de facto standard base64
      specification when the XML-RPC spec was written.

   また、 "__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 オブジェクト
===========================

class xmlrpclib.Fault

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

   faultCode

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

   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 as err:
       print "A fault occurred"
       print "Fault code: %d" % err.faultCode
       print "Fault string: %s" % err.faultString


20.23.6. ProtocolError オブジェクト
===================================

class xmlrpclib.ProtocolError

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

   url

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

   errcode

      エラーコード。

   errmsg

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

   headers

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

In the following example we're going to intentionally cause a
"ProtocolError" by providing a URI that doesn't point to an XMLRPC
server:

   import xmlrpclib

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

   try:
       proxy.some_method()
   except xmlrpclib.ProtocolError as 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" を返
   し、呼び出したい手続き名とパラメタを "MultiCall" オブジェクトに保存
   するだけに留まります。オブジェクト自体を呼び出すと、それまでに保存
   しておいたすべての呼び出しを単一の "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 as v:
       print "ERROR", v

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

   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.HTTPConnection(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.ServerProxy('http://time.xmlrpc.com/RPC2', transport=p)
   print server.currentTime.getCurrentTime()


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

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

-[ 脚注 ]-

[1] This approach has been first presented in a discussion on
    xmlrpc.com.
