# `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` は必要な証明書検証とホスト名チェックを全てデフォルトで行います。

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')]`). 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メソッド呼び出し時に接続処理の一部としてリモートサーバに送信されます。リモートサーバが基本認証を要求する場合のみ、この機能を利用する必要があります。HTTPS URL が与えられたなら、 context は `ssl.SSLContext` にでき、基底のHTTP接続のSSL設定を設定します。

生成されるインスタンスはリモートサーバへのプロクシオブジェクトで、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 オブジェクト

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

予約属性 `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` 値を出力します。

  また、比較と `__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"))

## 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__()` メソッドで定義される演算子を使用することができます。

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

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

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

    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)

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

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

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

脚注

1

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