21.26. "xmlrpc.client" --- XML-RPC クライアントアクセス
*******************************************************

**ソースコード:** Lib/xmlrpc/client.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.

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

バージョン 3.5 で変更: HTTPS URI の場合、 "xmlrpc.client" は必要な証明
書検証とホスト名チェックを全てデフォルトで行います。

class xmlrpc.client.ServerProxy(uri, transport=None, encoding=None, verbose=False, allow_none=False, use_datetime=False, use_builtin_types=False, *, context=None)

   バージョン 3.3 で変更: *use_builtin_types* フラグが追加されました。

   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_builtin_types* flag can be used to cause date/time values
   to be presented as "datetime.datetime" objects and binary data to
   be presented as "bytes" objects; this flag is false by default.
   "datetime.datetime", "bytes" and "bytearray" objects may be passed
   to calls. The obsolete *use_datetime* flag is similar to
   *use_builtin_types* but it applies only to date/time values.

   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", "i1", "i2",     | "int" in range from -2147483648 to 2147483647. Values   |
   | "i4", "i8" or          | get the "<int>" tag.                                    |
   | "biginteger"           |                                                         |
   +------------------------+---------------------------------------------------------+
   | "double" or "float"    | "float".  Values get the "<double>" tag.                |
   +------------------------+---------------------------------------------------------+
   | "string"               | "str"                                                   |
   +------------------------+---------------------------------------------------------+
   | "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" または "datetime.datetime"。返される型は     |
   |                        | *use_builtin_types* および *use_datetime* の値に依りま  |
   |                        | す。                                                    |
   +------------------------+---------------------------------------------------------+
   | "base64"               | "Binary"、 "bytes" または "bytearray"。返される型は     |
   |                        | *use_builtin_types* フラグの値に依ります。              |
   +------------------------+---------------------------------------------------------+
   | "nil"                  | "None" 定数。 *allow_none* が真の場合にのみ渡すことが出 |
   |                        | 来ます。                                                |
   +------------------------+---------------------------------------------------------+
   | "bigdecimal"           | "decimal.Decimal".  Returned type only.                 |
   +------------------------+---------------------------------------------------------+

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

   When passing strings, characters special to XML such as "<", ">",
   and "&" will be automatically escaped.  However, it's the caller's
   responsibility to ensure that the string is free of characters that
   aren't allowed in XML, such as the control characters with ASCII
   values between 0 and 31 (except, of course, tab, newline and
   carriage return); failing to do this will result in an XML-RPC
   request that isn't well-formed XML.  If you have to pass arbitrary
   bytes via XML-RPC, use "bytes" or "bytearray" classes or the
   "Binary" wrapper class described below.

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

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

   バージョン 3.6 で変更: Added support of type tags with prefixes
   (e.g. "ex:nil"). Added support of unmarshalling additional types
   used by Apache XML-RPC implementation for numerics: "i1", "i2",
   "i8", "biginteger", "float" and "bigdecimal". See
   http://ws.apache.org/xmlrpc/types.html for a description.

参考:

  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."


21.26.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マークアップが含まれます。

バージョン 3.5 で変更: Instances of "ServerProxy" support the *context
manager* protocol for closing the underlying transport.

以下は、動作する例です。サーバ側のコード:

   from xmlrpc.server 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 xmlrpc.client

   with xmlrpc.client.ServerProxy("http://localhost:8000/") as proxy:
       print("3 is even: %s" % str(proxy.is_even(3)))
       print("100 is even: %s" % str(proxy.is_even(100)))


21.26.2. DateTime オブジェクト
==============================

class xmlrpc.client.DateTime

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

   decode(string)

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

   encode(out)

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

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

以下は、動作する例です。サーバ側のコード:

   import datetime
   from xmlrpc.server import SimpleXMLRPCServer
   import xmlrpc.client

   def today():
       today = datetime.datetime.today()
       return xmlrpc.client.DateTime(today)

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

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

   import xmlrpc.client
   import datetime

   proxy = xmlrpc.client.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"))


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

class xmlrpc.client.Binary

   このクラスは、バイト列データ(NULを含む)で初期化することができます。
   "Binary" の内容は、属性で参照します:

   data

      "Binary" インスタンスがカプセル化しているバイナリデータ。このデ
      ータは "bytes" オブジェクトです。

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

   decode(bytes)

      指定されたbase64 "bytes" オブジェクトをデコードし、インスタンス
      のデータとします。

   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.

   また、 "__eq__()" および "__ne__()" メソッドで定義される演算子を使
   用することができます。

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

   from xmlrpc.server import SimpleXMLRPCServer
   import xmlrpc.client

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

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

   server.serve_forever()

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

   import xmlrpc.client

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


21.26.4. Fault オブジェクト
===========================

class xmlrpc.client.Fault

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

   faultCode

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

   faultString

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

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

   from xmlrpc.server 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 xmlrpc.client

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


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

class xmlrpc.client.ProtocolError

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

   url

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

   errcode

      エラーコード。

   errmsg

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

   headers

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

次の例では、不適切な URI を利用して、故意に "ProtocolError" を発生させ
ています:

   import xmlrpc.client

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

   try:
       proxy.some_method()
   except xmlrpc.client.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)


21.26.6. MultiCall オブジェクト
===============================

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

class xmlrpc.client.MultiCall(server)

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

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

   from xmlrpc.server 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 xmlrpc.client

   proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
   multicall = xmlrpc.client.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))


21.26.7. 補助関数
=================

xmlrpc.client.dumps(params, methodname=None, methodresponse=None, encoding=None, allow_none=False)

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

xmlrpc.client.loads(data, use_datetime=False, use_builtin_types=False)

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

   非推奨となった *use_datetime* フラグは *use_builtin_types* に似てい
   ますが date/time 値にのみ適用されます。

   バージョン 3.3 で変更: *use_builtin_types* フラグが追加されました。


21.26.8. クライアントのサンプル
===============================

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

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

       print(proxy)

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

To access an XML-RPC server through a HTTP proxy, you need to define a
custom transport.  The following example shows how:

   import http.client
   import xmlrpc.client

   class ProxiedTransport(xmlrpc.client.Transport):

       def set_proxy(self, host, port=None, headers=None):
           self.proxy = host, port
           self.proxy_headers = headers

       def make_connection(self, host):
           connection = http.client.HTTPConnection(*self.proxy)
           connection.set_tunnel(host, headers=self.proxy_headers)
           self._connection = host, connection
           return connection

   transport = ProxiedTransport()
   transport.set_proxy('proxy-server', 8080)
   server = xmlrpc.client.ServerProxy('http://betty.userland.com', transport=transport)
   print(server.examples.getStateName(41))


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

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

-[ 脚注 ]-

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