# `xmlrpc.client` --- XML-RPC クライアントアクセス

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

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

警告

The `xmlrpc.client` module is not secure against maliciously constructed data. If you need to parse untrusted or unauthenticated data, see XML security.

バージョン 3.5 で変更: For HTTPS URIs, `xmlrpc.client` now performs all the necessary certificate and hostname checks by default.

Availability: 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 番目の引数はデバッグフラグです。

  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 headers parameter is an optional sequence of HTTP headers to send with each request, expressed as a sequence of 2-tuples representing the header name and value. (e.g. `[('Header-Name', 'value')]`). If an HTTPS URL is provided, context may be `ssl.SSLContext` and configures the SSL settings of the underlying HTTPS connection. The obsolete use_datetime flag is similar to use_builtin_types but it applies only to date/time values.

  バージョン 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`, `i4`, `i8` または `biginteger` | `int` in range from -2147483648 to 2147483647. Values get the `<int>` tag.
  `double` または `float` | `float`. Values get the `<double>` tag.
  `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`. Returned type only.

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

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

  `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 https://ws.apache.org/xmlrpc/types.html for a description.

参考

XML-RPC HOWTO

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

XML-RPC Introspection

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

XML-RPC Specification

  公式の仕様

## ServerProxy オブジェクト

A `ServerProxy` instance has a method corresponding to each remote procedure call accepted by the XML-RPC server. Calling the method performs an RPC, dispatched by both name and argument signature (e.g. the same method name can be overloaded with multiple argument signatures). The RPC finishes either by returning data in a conformant type or by raising a `Fault` or `ProtocolError` exception indicating an error.

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

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

## DateTime オブジェクト

class xmlrpc.client.DateTime

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

  decode(string)

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

  encode(out)

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

  It also supports certain of Python's built-in operators through `rich comparison` and `__repr__()` methods.

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

    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()
    # convert the ISO 8601 string to a datetime object
    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です。

  It also supports certain of Python's built-in operators through `__eq__()` and `__ne__()` methods.

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

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

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

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

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

## 補助関数

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 フラグが追加されました。

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

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

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 example を参照してください。

脚注

[1]

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