xmlrpc.client — Клиентский доступ к XML-RPC

Источник: Lib/xmlrpc/client.py


XML-RPC - это метод удаленного вызова процедур, использующий в качестве транспорта XML, передаваемый по протоколу HTTP(S). С его помощью клиент может вызывать методы с параметрами на удаленном сервере (сервер именуется URI) и получать обратно структурированные данные. Этот модуль поддерживает написание клиентского кода XML-RPC; он обрабатывает все детали трансляции между конформными объектами Python и XML на проводе.

Предупреждение

Модуль xmlrpc.client не защищен от злонамеренно созданных данных. Если вам нужно разобрать недоверенные или неаутентифицированные данные, смотрите Уязвимости XML.

Изменено в версии 3.5: Для HTTPS URI xmlrpc.client теперь по умолчанию выполняет все необходимые проверки сертификата и имени хоста.

Availability: не 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-адрес сервера. Необязательный второй аргумент - экземпляр транспортной фабрики; по умолчанию это внутренний экземпляр SafeTransport для URL https: и внутренний экземпляр HTTP Transport в противном случае. Необязательный третий аргумент - кодировка, по умолчанию UTF-8. Необязательный четвертый аргумент - флаг отладки.

Следующие параметры определяют использование возвращаемого экземпляра прокси. Если allow_none равен true, константа Python None будет переведена в XML; по умолчанию None вызывает ошибку TypeError. Это часто используемое расширение спецификации XML-RPC, но поддерживается не всеми клиентами и серверами; описание см. в http://ontosys.com/xml-rpc/extensions.php. Флаг use_builtin_types может использоваться для того, чтобы значения даты/времени представлялись как объекты datetime.datetime, а двоичные данные - как объекты bytes; по умолчанию этот флаг равен false. В вызовы могут передаваться объекты datetime.datetime, bytes и bytearray. Параметр headers - это необязательная последовательность HTTP-заголовков для отправки с каждым запросом, выраженная в виде последовательности 2-кортежей, представляющих имя и значение заголовка. (например, [('Header-Name', 'value')]). Устаревший флаг use_datetime аналогичен use_builtin_types, но применяется только к значениям даты/времени.

Изменено в версии 3.3: Был добавлен флаг use_builtin_types.

Изменено в версии 3.8: Был добавлен параметр headers.

Оба транспорта HTTP и HTTPS поддерживают расширение синтаксиса URL для базовой аутентификации HTTP: http://user:pass@host:port/path. Часть user:pass будет закодирована в base64 как заголовок HTTP „Authorization“ и отправлена на удаленный сервер как часть процесса соединения при вызове метода XML-RPC. Это нужно использовать только в том случае, если удаленный сервер требует пользователя и пароля базовой аутентификации. Если указан HTTPS-адрес, context может быть ssl.SSLContext и настраивает параметры SSL для базового HTTPS-соединения.

Возвращаемый экземпляр представляет собой прокси-объект с методами, которые можно использовать для вызова соответствующих RPC-вызовов на удаленном сервере. Если удаленный сервер поддерживает API интроспекции, прокси также можно использовать для запроса методов, которые он поддерживает (обнаружение сервиса), и получения других метаданных, связанных с сервером.

Типы, которые являются совместимыми (например, которые могут быть отображены в XML), включают следующие (за исключением тех случаев, когда это отмечено, они не отображаются как один и тот же тип Python):

Тип XML-RPC

Тип Python

boolean

bool

int, i1, i2, i4, i8 или biginteger.

int в диапазоне от -2147483648 до 2147483647. Значения получают тег <int>.

double или float

float. Значения получают тег <double>.

string

str

array

list или tuple, содержащие конформные элементы. Массивы возвращаются в виде lists.

struct

dict. Ключи должны быть строками, значения могут быть любого совместимого типа. Можно передавать объекты пользовательских классов; передается только их атрибут __dict__.

dateTime.iso8601

DateTime или datetime.datetime. Возвращаемый тип зависит от значений флагов use_builtin_types и use_datetime.

base64

Binary, bytes или bytearray. Возвращаемый тип зависит от значения флага use_builtin_types.

nil

Константа None. Передача разрешена только в том случае, если allow_none равно true.

bigdecimal

decimal.Decimal. Возвращается только тип.

Это полный набор типов данных, поддерживаемых XML-RPC. Вызовы методов могут также вызывать специальный экземпляр Fault, используемый для сигнализации об ошибках сервера XML-RPC, или ProtocolError, используемый для сигнализации об ошибке на транспортном уровне HTTP/HTTPS. И Fault, и ProtocolError происходят от базового класса Error. Обратите внимание, что в настоящее время клиентский модуль xmlrpc не маршализирует экземпляры подклассов встроенных типов.

При передаче строк символы, характерные для XML, такие как <, > и &, будут автоматически экранированы. Однако вызывающая сторона несет ответственность за то, чтобы в строке не было символов, запрещенных в XML, таких как управляющие символы со значениями ASCII от 0 до 31 (кроме, конечно, табуляции, новой строки и возврата каретки); если этого не сделать, то XML-RPC-запрос не будет хорошо сформированным XML. Если вам нужно передать произвольные байты через XML-RPC, используйте классы 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, указывающим на ошибку.

Серверы, поддерживающие API интроспекции XML, поддерживают некоторые общие методы, сгруппированные под зарезервированным атрибутом system:

ServerProxy.system.listMethods()

Этот метод возвращает список строк, по одной на каждый (несистемный) метод, поддерживаемый сервером XML-RPC.

ServerProxy.system.methodSignature(name)

Этот метод принимает один параметр - имя метода, реализованного сервером XML-RPC. Он возвращает массив возможных сигнатур для этого метода. Подпись - это массив типов. Первый из этих типов - возвращаемый тип метода, остальные - параметры.

Поскольку разрешено использование нескольких сигнатур (т.е. перегрузка), этот метод возвращает список сигнатур, а не синглтон.

Сами сигнатуры ограничиваются параметрами верхнего уровня, ожидаемыми методом. Например, если метод ожидает в качестве параметра один массив структур, а возвращает строку, его сигнатура будет просто «string, array». Если он ожидает три целых числа и возвращает строку, его сигнатура будет «string, int, int, int».

Если для метода не определена сигнатура, возвращается значение, не являющееся массивом. В Python это означает, что тип возвращаемого значения будет не списком, а чем-то другим.

ServerProxy.system.methodHelp(name)

Этот метод принимает один параметр - имя метода, реализованного сервером XML-RPC. Он возвращает строку документации, описывающую использование этого метода. Если такой строки нет, возвращается пустая строка. Строка документации может содержать HTML-разметку.

Изменено в версии 3.5: Экземпляры ServerProxy поддерживают протокол context manager для закрытия базового транспорта.

Ниже приведен рабочий пример. Код сервера:

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)

Запишите XML-RPC-кодировку этого DateTime элемента в объект потока out.

Он также поддерживает некоторые встроенные операторы Python с помощью методов rich comparison и __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"))

Бинарные объекты

class xmlrpc.client.Binary

Этот класс может быть инициализирован из байтовых данных (которые могут включать NUL). Основной доступ к содержимому объекта Binary обеспечивается атрибутом:

data

Двоичные данные, заключенные в экземпляре Binary. Данные предоставляются в виде объекта bytes.

Объекты Binary имеют следующие методы, которые поддерживаются в основном для внутреннего использования кодом маршаллинга и размаршаллинга:

decode(bytes)

Принимает объект base64 bytes и декодирует его как новые данные экземпляра.

encode(out)

Запишите кодировку XML-RPC base 64 этого двоичного элемента в объект out stream.

Закодированные данные будут иметь новые строки через каждые 76 символов в соответствии с RFC 2045 section 6.8, который был де-факто стандартной спецификацией base64 на момент написания спецификации XML-RPC.

Он также поддерживает некоторые встроенные операторы Python с помощью методов __eq__() и __ne__().

Пример использования бинарных объектов. Мы собираемся передать изображение через XMLRPC:

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)

Объекты неисправностей

class xmlrpc.client.Fault

Объект Fault заключает в себе содержимое тега неисправности XML-RPC. Объекты неисправностей имеют следующие атрибуты:

faultCode

Число int, указывающее тип ошибки.

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 описывает ошибку протокола на базовом транспортном уровне (например, ошибку 404 «не найден», если сервер, указанный в URI, не существует). Он имеет следующие атрибуты:

url

URI или URL, вызвавший ошибку.

errcode

Код ошибки.

errmsg

Сообщение об ошибке или диагностическая строка.

headers

Диктант, содержащий заголовки HTTP/HTTPS-запроса, вызвавшего ошибку.

В следующем примере мы намеренно вызываем ошибку ProtocolError, предоставляя неверный URI:

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 предоставляет возможность инкапсулировать несколько обращений к удаленному серверу в один запрос [1].

class xmlrpc.client.MultiCall(server)

Создайте объект, используемый для вызова метода boxcar. Объект server является конечной целью вызова. Вызовы могут быть сделаны к объекту result, но они немедленно вернут 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))

Удобные функции

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

Преобразует params в XML-RPC-запрос. или в ответ, если methodresponse равен true. params может быть либо кортежем аргументов, либо экземпляром класса исключений Fault. Если methodresponse имеет значение true, может быть возвращено только одно значение, то есть params должны иметь длину 1. encoding, если указано, - это кодировка, которую следует использовать в сгенерированном XML; по умолчанию это UTF-8. Питоновское значение None не может использоваться в стандартном XML-RPC; чтобы разрешить его использование через расширение, укажите значение true для allow_none.

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

Преобразование XML-RPC-запроса или ответа в объекты Python, а (params, methodname). params - кортеж аргументов; methodname - строка, или None, если в пакете нет имени метода. Если пакет XML-RPC представляет собой состояние ошибки, эта функция вызовет исключение Fault. Флаг use_builtin_types может быть использован для того, чтобы значения даты/времени представлялись как объекты datetime.datetime, а двоичные данные - как объекты bytes; по умолчанию этот флаг равен false.

Устаревший флаг use_datetime похож на use_builtin_types, но применяется только к значениям даты/времени.

Изменено в версии 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.

Сноски