13.11. xmlrpc.client — xml-江南app体育官方入口

未匹配的标注

目的:用于xml-rpc通信的客户端库。

xml-rpc是建立在http和xml之上的轻量级远程过程调用协议。 xmlrpclib模块允许python程序与以任何语言编写的xml-rpc服务器进行通信。

本节中的所有示例均使用xmlrpc_server.py中定义的服务器,该服务器在源代码发行版中可用,并包含在此处以供参考。

xmlrpc_server.py

from xmlrpc.server import simplexmlrpcserver
from xmlrpc.client import binary
import datetime
class exampleservice:
    def ping(self):
        """simple function to respond when called
        to demonstrate connectivity.
        """
        return true
    def now(self):
        """returns the server current date and time."""
        return datetime.datetime.now()
    def show_type(self, arg):
        """illustrates how types are passed in and out of
        server methods.
        accepts one argument of any type.
        returns a tuple with string representation of the value,
        the name of the type, and the value itself.
        """
        return (str(arg), str(type(arg)), arg)
    def raises_exception(self, msg):
        "always raises a runtimeerror with the message passed in"
        raise runtimeerror(msg)
    def send_back_binary(self, bin):
        """accepts single binary argument, and unpacks and
        repacks it to return it."""
        data = bin.data
        print('send_back_binary({!r})'.format(data))
        response = binary(data)
        return response
if __name__ == '__main__':
    server = simplexmlrpcserver(('localhost', 9000),
                                logrequests=true,
                                allow_none=true)
    server.register_introspection_functions()
    server.register_multicall_functions()
    server.register_instance(exampleservice())
    try:
        print('use control-c to exit')
        server.serve_forever()
    except keyboardinterrupt:
        print('exiting')

连接到服务器

将客户端连接到服务器的最简单方法是实例化serverproxy对象,并为其提供服务器的uri。例如,演示服务器在本地主机的端口9000上运行。

xmlrpc_serverproxy.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000')
print('ping:', server.ping())

在这种情况下,服务的ping() 方法不带任何参数并返回单个布尔值。

$ python3 xmlrpc_serverproxy.py
ping: true

其他选项可用于支持替代运输。现成的同时支持 http 和 https,并且都具有基本身份验证。为了实现新的通信通道,只需要一个新的传输类。例如,通过 smtp 实现 xml-rpc 可能是一个有趣的练习。

xmlrpc_serverproxy_verbose.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000',
                                   verbose=true)
print('ping:', server.ping())

verbose选项提供调试信息,可用于解决通信错误。

$ python3 xmlrpc_serverproxy_verbose.py
send: b'post /rpc2 http/1.1..host: localhost:9000..
accept-encoding: gzip..content-type: text/xml..
user-agent: python-xmlrpc/3.5..content-length: 98....'
send: b"..
ping...."
reply: 'http/1.0 200 ok..'
header: server header: date header: content-type header:
content-length body: b"..
..1.
..."
ping: true

如果需要备用系统,则可以从 utf-8 更改默认编码。

xmlrpc_serverproxy_encoding.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000',
                                   encoding='iso-8859-1')
print('ping:', server.ping())

服务器自动检测正确的编码。

$ python3 xmlrpc_serverproxy_encoding.py
ping: true

allow_none选项控制python的none值是否自动转换为nil值或是否导致错误。

xmlrpc_serverproxy_allow_none.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000',
                                   allow_none=false)
try:
    server.show_type(none)
except typeerror as err:
    print('error:', err)
server = xmlrpc.client.serverproxy('http://localhost:9000',
                                   allow_none=true)
print('allowed:', server.show_type(none))

如果客户端不允许none,则会在本地引发错误,但如果未将其配置为允许none,则也可以从服务器内部引发该错误。

$ python3 xmlrpc_serverproxy_allow_none.py
error: cannot marshal none unless allow_none is enabled
allowed: ['none', "", none]

数据类型

xml-rpc 协议可识别一组有限的通用数据类型。这些类型可以作为参数或返回值传递,并可以组合以创建更复杂的数据结构。

xmlrpc_types.py

import xmlrpc.client
import datetime
server = xmlrpc.client.serverproxy('http://localhost:9000')
data = [
    ('boolean', true),
    ('integer', 1),
    ('float', 2.5),
    ('string', 'some text'),
    ('datetime', datetime.datetime.now()),
    ('array', ['a', 'list']),
    ('array', ('a', 'tuple')),
    ('structure', {'a': 'dictionary'}),
]
for t, v in data:
    as_string, type_name, value = server.show_type(v)
    print('{:<12}: {}'.format(t, as_string))
    print('{:12}  {}'.format('', type_name))
    print('{:12}  {}'.format('', value))

简单类型是:

$ python3 xmlrpc_types.py
boolean     : true
              
              true
integer     : 1
              
              1
float       : 2.5
              
              2.5
string      : some text
              
              some text
datetime    : 20160618t19:31:47
              
              20160618t19:31:47
array       : ['a', 'list']
              
              ['a', 'list']
array       : ['a', 'tuple']
              
              ['a', 'tuple']
structure   : {'a': 'dictionary'}
              
              {'a': 'dictionary'}

可以嵌套支持的类型以创建任意复杂度的值。

xmlrpc_types_nested.py

import xmlrpc.client
import datetime
import pprint
server = xmlrpc.client.serverproxy('http://localhost:9000')
data = {
    'boolean': true,
    'integer': 1,
    'floating-point number': 2.5,
    'string': 'some text',
    'datetime': datetime.datetime.now(),
    'array1': ['a', 'list'],
    'array2': ('a', 'tuple'),
    'structure': {'a': 'dictionary'},
}
arg = []
for i in range(3):
    d = {}
    d.update(data)
    d['integer'] = i
    arg.append(d)
print('before:')
pprint.pprint(arg, width=40)
print('.after:')
pprint.pprint(server.show_type(arg)[-1], width=40)

该程序将包含所有受支持类型的词典列表传递到示例服务器,该服务器返回数据。元组将转换为列表,并且datetime实例将转换为datetime对象,但否则数据将保持不变。

$ python3 xmlrpc_types_nested.py
before:
[{'array': ('a', 'tuple'),
  'boolean': true,
  'datetime': datetime.datetime(2016, 6, 18, 19, 27, 30, 45333),
  'floating-point number': 2.5,
  'integer': 0,
  'string': 'some text',
  'structure': {'a': 'dictionary'}},
 {'array': ('a', 'tuple'),
  'boolean': true,
  'datetime': datetime.datetime(2016, 6, 18, 19, 27, 30, 45333),
  'floating-point number': 2.5,
  'integer': 1,
  'string': 'some text',
  'structure': {'a': 'dictionary'}},
 {'array': ('a', 'tuple'),
  'boolean': true,
  'datetime': datetime.datetime(2016, 6, 18, 19, 27, 30, 45333),
  'floating-point number': 2.5,
  'integer': 2,
  'string': 'some text',
  'structure': {'a': 'dictionary'}}]
after:
[{'array': ['a', 'tuple'],
  'boolean': true,
  'datetime': ,
  'floating-point number': 2.5,
  'integer': 0,
  'string': 'some text',
  'structure': {'a': 'dictionary'}},
 {'array': ['a', 'tuple'],
  'boolean': true,
  'datetime': ,
  'floating-point number': 2.5,
  'integer': 1,
  'string': 'some text',
  'structure': {'a': 'dictionary'}},
 {'array': ['a', 'tuple'],
  'boolean': true,
  'datetime': ,
  'floating-point number': 2.5,
  'integer': 2,
  'string': 'some text',
  'structure': {'a': 'dictionary'}}]

xml-rpc 支持将日期作为本机类型,并且xmlrpclib可以使用两个类之一来表示传出代理中或从服务器接收到的日期值。

xmlrpc_serverproxy_use_datetime.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000',
                                   use_datetime=true)
now = server.now()
print('with:', now, type(now), now.__class__.__name__)
server = xmlrpc.client.serverproxy('http://localhost:9000',
                                   use_datetime=false)
now = server.now()
print('without:', now, type(now), now.__class__.__name__)

默认情况下,使用内部版本的datetime,但是use_datetime选项打开支持使用模块。

$ python3 source/xmlrpc.client/xmlrpc_serverproxy_use_datetime.py
with: 2016-06-18 19:18:31  datetime
without: 20160618t19:18:31  datetime

传递对象

python 类的实例被视为结构,并作为字典传递,而对象的属性作为字典中的值。

xmlrpc_types_object.py

import xmlrpc.client
import pprint
class myobj:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __repr__(self):
        return 'myobj({!r}, {!r})'.format(self.a, self.b)
server = xmlrpc.client.serverproxy('http://localhost:9000')
o = myobj(1, 'b goes here')
print('o  :', o)
pprint.pprint(server.show_type(o))
o2 = myobj(2, o)
print('.o2 :', o2)
pprint.pprint(server.show_type(o2))

传递对象

python 类的实例被视为结构,并作为字典传递,而对象的属性作为字典中的值。

xmlrpc_types_object.py

导入xmlrpc.client
导入pprint
myobj类:
    def __init __(self,a,b):
        self.a = a
        self.b = b
    def __repr __(自己):
        返回'myobj({!r},{!r})'。format(self.a,self.b)
服务器= xmlrpc.client.serverproxy('http:/// localhost:9000')
o = myobj(1,'b去这里')
打印('o:',o)
pprint.pprint(server.show_type(o))
o2 = myobj(2,o)
print('。o2:',o2)
pprint.pprint(server.show_type(o2))

当该值从服务器发送回客户端时,结果是客户端上的字典,因为值中没有任何编码来告诉服务器(或客户端)应将其实例化为类的一部分。

$ python3 xmlrpc_types_object.py
o  : myobj(1, 'b goes here')
["{'b': 'b goes here', 'a': 1}", "",
{'a': 1, 'b': 'b goes here'}]
o2 : myobj(2, myobj(1, 'b goes here'))
["{'b': {'b': 'b goes here', 'a': 1}, 'a': 2}",
 "",
 {'a': 2, 'b': {'a': 1, 'b': 'b goes here'}}]

二进制数据

传递给服务器的所有值都将被编码并自动转义。但是,某些数据类型可能包含无效的 xml 字符。例如,二进制图像数据可能包括 ascii 控制范围为 0 到 31 的字节值。要传递二进制数据,最好使用binary类对其进行编码以进行传输。

xmlrpc_binary.py

import xmlrpc.client
import xml.parsers.expat
server = xmlrpc.client.serverproxy('http://localhost:9000')
s = b'this is a string with control characters.00'
print('local string:', s)
data = xmlrpc.client.binary(s)
response = server.send_back_binary(data)
print('as binary:', response.data)
try:
    print('as string:', server.show_type(s))
except xml.parsers.expat.expaterror as err:
    print('.error:', err)

如果将包含 null 字节的字符串传递给show_type(),则 xml 解析器在处理响应时会引发异常。

$ python3 xmlrpc_binary.py
local string: b'this is a string with control characters.00'
as binary: b'this is a string with control characters.00'
error: not well-formed (invalid token): line 6, column 55

binary 对象还可以用于通过 。与通过网络发送多少可执行代码有关的常规安全问题在这里适用(即除非通信通道安全,否则不要这样做)。


import xmlrpc.client
import pickle
import pprint
class myobj:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __repr__(self):
        return 'myobj({!r}, {!r})'.format(self.a, self.b)
server = xmlrpc.client.serverproxy('http://localhost:9000')
o = myobj(1, 'b goes here')
print('local:', id(o))
print(o)
print('.as object:')
pprint.pprint(server.show_type(o))
p = pickle.dumps(o)
b = xmlrpc.client.binary(p)
r = server.send_back_binary(b)
o2 = pickle.loads(r.data)
print('.from pickle:', id(o2))
pprint.pprint(o2)

binary实例的data属性包含该对象的 pickled 版本,因此必须先对其进行 pickled 才能使用它。这导致另一个对象(具有新的id值)。

$ python3 xmlrpc_binary_pickle.py
local: 4327262304
myobj(1, 'b goes here')
as object:
["{'a': 1, 'b': 'b goes here'}", "",
{'a': 1, 'b': 'b goes here'}]
from pickle: 4327262472
myobj(1, 'b goes here')

异常处理

由于 xml-rpc 服务器可以用任何语言编写,因此无法直接传输异常类。相反,服务器中引发的异常将转换为fault对象,并在客户端本地作为异常引发。

xmlrpc_exception.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000')
try:
    server.raises_exception('a message')
except exception as err:
    print('fault code:', err.faultcode)
    print('message   :', err.faultstring)

原始错误消息保存在faultstring属性中,并且faultcode设置为 xml-rpc 错误号。

$ python3 xmlrpc_exception.py
fault code: 1
message   : :a message

将呼叫合并为一条消息

多重调用是 xml-rpc 协议的扩展,它允许同时发送多个调用,并收集响应并将其返回给调用者。

xmlrpc_multicall.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000')
multicall = xmlrpc.client.multicall(server)
multicall.ping()
multicall.show_type(1)
multicall.show_type('string')
for i, r in enumerate(multicall()):
    print(i, r)

要使用multicall实例,请像使用serverproxy一样调用其上的方法,然后调用不带参数的对象以实际运行远程功能。返回值是一个迭代器,它从所有调用中产生结果。

$ python3 xmlrpc_multicall.py
0 true
1 ['1', "", 1]
2 ['string', "", 'string']

如果其中一个调用导致fault,则当从迭代器生成结果并且没有更多结果可用时,将引发异常。

xmlrpc_multicall_exception.py

import xmlrpc.client
server = xmlrpc.client.serverproxy('http://localhost:9000')
multicall = xmlrpc.client.multicall(server)
multicall.ping()
multicall.show_type(1)
multicall.raises_exception('next to last call stops execution')
multicall.show_type('string')
try:
    for i, r in enumerate(multicall()):
        print(i, r)
except xmlrpc.client.fault as err:
    print('error:', err)

由于来自raises_exception()的第三个响应会生成异常,因此无法访问来自show_type()的响应。

$ python3 xmlrpc_multicall_exception.py
0 true
1 ['1', "", 1]
error: :next to last call stops execution">

也可以看看

  •  – xml-rpc服务器实现。
  •  – http服务器实施。
  •  – 介绍如何使用xml-rpc来以多种语言实现客户端和服务器。

本文章首发在 江南app体育官方入口 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系江南app体育官方入口。

原文地址:https://learnku.com/docs/pymotw/xmlrpccl...

译文地址:https://learnku.com/docs/pymotw/xmlrpccl...

上一篇 下一篇
贡献者:2
讨论数量: 0



暂无话题~
网站地图