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

**ソースコード:** Lib/xmlrpc/client.py

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

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

警告:

  "xmlrpc.client" モジュールは、悪意を持って作成されたデータに対して安
  全ではありません。 信頼できないデータや認証されていないデータを解析
  する必要がある場合は、XML のセキュリティ を参照してください。

バージョン 3.5 で変更: HTTPS URI に対して、"xmlrpc.client" は必要な証
明書とホスト名の検証をデフォルトですべて行うようになりました。

利用可能な環境: not WASI.

このモジュールは WebAssembly では動作しないか、利用不可です。詳しくは
、WebAssembly プラットフォーム を見てください。

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

   "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.php を参照してください。
   *use_builtin_types* フラグを使うと、日時の値を "datetime.datetime"
   オブジェクト、バイナリデータを "bytes" オブジェクトとして受け取れま
   す。 このフラグのデフォルトは偽です。 呼び出しには
   "datetime.datetime"、"bytes"、"bytearray" オブジェクトを渡せます。
   *headers* は各リクエストで送信する HTTP ヘッダーを指定する省略可能
   な引数で、ヘッダー名と値を表す 2 要素のタプルのシーケンス (例えば
   "[('Header-Name', 'value')]") で指定します。 HTTPS URL を指定した場
   合、*context* に "ssl.SSLContext" を指定して、内部の HTTPS 接続の
   SSL 設定を構成できます。 旧式の *use_datetime* フラグは
   *use_builtin_types* と似ていますが、日時の値にだけ適用されます。

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

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

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

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

   以下の型をXMLに変換(XMLを通じてマーシャルする)する事ができます (特
   別な指定がない限り、逆変換でも同じ型として変換されます):

   +------------------------+---------------------------------------------------------+
   | XML-RPC の型           | Python の型                                             |
   |========================|=========================================================|
   | "boolean"              | "bool"                                                  |
   +------------------------+---------------------------------------------------------+
   | "int", "i1", "i2",     | -2147483648 から 2147483647 までの範囲の "int"。 値には |
   | "i4", "i8" または      | "<int>" タ グが付けられます。                           |
   | "biginteger"           |                                                         |
   +------------------------+---------------------------------------------------------+
   | "double" または        | "float"。 値には "<double>" タグが付けられます。        |
   | "float"                |                                                         |
   +------------------------+---------------------------------------------------------+
   | "string"               | "str"                                                   |
   +------------------------+---------------------------------------------------------+
   | "array"                | 適合する要素を持つ "list" または "tuple"。array は      |
   |                        | "list" として返 します。                                |
   +------------------------+---------------------------------------------------------+
   | "struct"               | "dict"。 キーは文字列でなければならず、値には変換可能な |
   |                        | 任意の型を使 用できます。 ユーザー定義クラスのインスタ  |
   |                        | ンスも渡せますが、転送され るのは "__dict__" 属性だけで |
   |                        | す。                                                    |
   +------------------------+---------------------------------------------------------+
   | "dateTime.iso8601"     | "DateTime" または "datetime.datetime"。返される型は     |
   |                        | *use_builtin_types* および *use_datetime* の値に依りま  |
   |                        | す。                                                    |
   +------------------------+---------------------------------------------------------+
   | "base64"               | "Binary"、 "bytes" または "bytearray"。返される型は     |
   |                        | *use_builtin_types* フラグの値に依ります。              |
   +------------------------+---------------------------------------------------------+
   | "nil"                  | "None" 定数。 *allow_none* が真の場合にのみ渡すことが出 |
   |                        | 来ます。                                                |
   +------------------------+---------------------------------------------------------+
   | "bigdecimal"           | "decimal.Decimal"。 返される値としてのみ使われます。    |
   +------------------------+---------------------------------------------------------+

   以上が XML-RPC でサポートされるデータ型のすべてです。 メソッド呼び
   出しでは、XML-RPC サーバーのエラーを表す "Fault" インスタンスや、
   HTTP/HTTPS トランスポート層のエラーを表す "ProtocolError" が送出さ
   れることもあります。 "Fault" と "ProtocolError" は、どちらも
   "Error" という基底クラスから派生しています。 現在の xmlrpc クライア
   ントモジュールは、組み込み型のサブクラスのインスタンスをマーシャル
   できないことに注意してください。

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

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

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

   バージョン 3.6 で変更: 接頭辞付きの型タグ ("ex:nil" など) をサポー
   トしました。 Apache の XML-RPC 実装が数値に使う追加の型 "i1"、"i2"
   、"i8"、"biginteger"、"float"、"bigdecimal" のアンマーシャルをサポ
   ートしました。 説明は https://ws.apache.org/xmlrpc/types.html を参
   照してください。

参考:

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

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

  XML-RPC Specification
     公式の仕様


ServerProxy オブジェクト
========================

"ServerProxy" インスタンスには、XML-RPC サーバーが受け付ける各リモート
手続き呼び出しに対応するメソッドがあります。 メソッドを呼び出すと、名
前と引数のシグネチャの両方に基づいて RPC が実行されます (例えば、同じ
メソッド名に対して引数のシグネチャが異なる複数の処理をオーバーロードで
きます)。 RPC は対応する型のデータを返すか、エラーを示す "Fault" また
は "ProtocolError" 例外を送出して終了します。

XML イントロスペクション API をサポートするサーバーは、予約された
"system" 属性の下にまとめられた共通のメソッドを提供します。

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 サーバーが実装するメソッドの名前を引数として 1 つ受け取り、
   そのメソッドの使い方を説明する文書文字列を返します。 そのような文字
   列がない場合は空文字列を返します。 文書文字列には HTML マークアップ
   が含まれることがあります。

バージョン 3.5 で変更: "ServerProxy" インスタンスは、内部のトランスポ
ートを閉じるための *コンテキストマネージャー* プロトコルをサポートしま
した。

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

   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)))


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

class xmlrpc.client.DateTime

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

   decode(string)

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

   encode(out)

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

   "拡張比較" メソッドと "__repr__()" メソッドを通じて、Python の組み
   込み演算子の一部もサポートしています。

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

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

   def today():
       today = dt.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 as dt

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

   today = proxy.today()
   # ISO 8601 形式の文字列を datetime オブジェクトに変換する
   converted = dt.datetime.strptime(today.value, "%Y%m%dT%H:%M:%S")
   print(f"Today: {converted.strftime('%d.%m.%Y, %H:%M')}")


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

class xmlrpc.client.Binary

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

   data

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

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

   decode(bytes)

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

   encode(out)

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

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

   "__eq__()" メソッドと "__ne__()" メソッドを通じて、Python の組み込
   み演算子の一部もサポートしています。

バイナリオブジェクトの使用例です。 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)


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

class xmlrpc.client.Fault

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

   faultCode

      失敗のタイプを示す整数。

   faultString

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

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

   from xmlrpc.server import SimpleXMLRPCServer

   # 複素数を返すため、マーシャル時にエラーが発生する
   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)


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

class xmlrpc.client.ProtocolError

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

   url

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

   errcode

      エラーコード。

   errmsg

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

   headers

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

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

   import xmlrpc.client

   # XMLRPC リクエストに応答しない URI で ServerProxy を作成する
   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)


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

"MultiCall" オブジェクトは遠隔のサーバに対する複数の呼び出しをひとつの
リクエストにカプセル化する方法を提供します [1]。

class xmlrpc.client.MultiCall(server)

   Create an object used to boxcar method calls. *server* is the
   eventual target of the call. Calls can be made to the result
   object, but they will immediately return "None", and only store the
   call name and arguments in the "MultiCall" object. Calling the
   object itself causes all stored calls to be transmitted as a single
   "system.multicall" request. The result of this call is a
   *generator*; iterating over this generator yields the individual
   results.

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

   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

   # 簡単な算術関数を持つシンプルなサーバー
   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))


補助関数
========

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

   Convert *params* into an XML-RPC request, or into a response if
   *methodresponse* is true. *params* can be either a tuple of
   arguments or an instance of the "Fault" exception class.  If
   *methodresponse* is true, only a single value can be returned,
   meaning that *params* must be of length 1. *encoding*, if supplied,
   is the encoding to use in the generated XML; the default is UTF-8.
   Python's "None" value cannot be used in standard XML-RPC; to allow
   using it via an extension,  provide a true value for *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* フラグが追加されました。


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

   # 簡単なテストプログラム (XML-RPC 仕様から引用)
   from xmlrpc.client import ServerProxy, Error

   # server = ServerProxy("http://localhost:8000") # ローカルサーバー
   with ServerProxy("http://betty.userland.com") as proxy:

       print(proxy)

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

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

   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))


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

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

-[ 脚注 ]-

[1] このアプローチは a discussion on xmlrpc.com にて最初に著されました
    。
