Документация Python неофициальный перевод
Содержание страницы

21.26. xmlrpc.client – клиентский доступ XML-RPCxmlrpc.client – XML-RPC client access

Исходный код: Lib/xmlrpc/client.py


XML-RPC – это метод удалённого вызова процедур (Remote Procedure Call), использующий XML, передаваемый через HTTP(S) в качестве транспорта. С его помощью клиент может вызывать методы с параметрами на удалённом сервере (сервер задаётся 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, *, context=None)

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

Экземпляр ServerProxy – это объект, управляющий связью с удалённым XML-RPC сервером. Обязательный первый аргумент – URI (Uniform Resource Indicator), обычно это URL сервера. Второй необязательный аргумент – экземпляр транспортной фабрики; по умолчанию для URL https: используется внутренний экземпляр SafeTransport, а в остальных случаях – внутренний экземпляр 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 могут передаваться в вызовы. Устаревший флаг use_datetime аналогичен use_builtin_types, но применяется только к значениям даты/времени.

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

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

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

Тип XML-RPC

Тип Python

boolean

bool

int, i1, i2, i4, i8 or 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.client в настоящее время не выполняет маршалинг экземпляров подклассов встроенных типов.

При передаче строк символы, специальные для 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. См. http://ws.apache.org/xmlrpc/types.html для описания.

См. также

Руководство по XML-RPC

Хорошее описание работы XML-RPC и клиентского ПО на нескольких языках. Содержит практически всё, что нужно знать разработчику XML-RPC клиента.

Интроспекция XML-RPC

Описывает расширение протокола XML-RPC для интроспекции.

Спецификация XML-RPC

Официальная спецификация.

Неофициальные исправления XML-RPC

«Неофициальные исправления» Фредрика Лунда, предназначенные для разъяснения некоторых деталей спецификации XML-RPC, а также для указания на «лучшие практики» при разработке собственных реализаций XML-RPC.

21.26.1. Объекты ServerProxyServerProxy Objects

Экземпляр 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 это означает, что тип возвращаемого значения будет чем-то отличным от list.

ServerProxy.system.methodHelp(name)

Этот метод принимает один параметр – имя метода, реализованного XML-RPC сервером. Он возвращает строку документации, описывающую использование этого метода. Если такая строка отсутствует, возвращается пустая строка. Строка документации может содержать 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)))

21.26.2. Объекты DateTimeDateTime Objects

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()
# преобразует строку в формате ISO8601 в объект datetime
converted = datetime.datetime.strptime(today.value, "%Y%m%dT%H:%M:%S")
print("Today: %s" % converted.strftime("%d.%m.%Y, %H:%M"))

21.26.3. Объекты BinaryBinary Objects

class xmlrpc.client.Binary

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

data

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

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

decode(bytes)

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

encode(out)

Записывает XML-RPC base64-кодировку этого двоичного объекта в потоковый объект out.

Закодированные данные будут содержать переводы строки каждые 76 символов в соответствии с RFC 2045, раздел 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)

21.26.4. Объекты FaultFault Objects

class xmlrpc.client.Fault

Объект Fault инкапсулирует содержимое тега ошибки (fault tag) 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)

21.26.5. Объекты ProtocolErrorProtocolError Objects

class xmlrpc.client.ProtocolError

Объект ProtocolError описывает протокольную ошибку на уровне нижележащего транспорта (например, ошибку 404 «не найдено», если сервер по указанному URI не существует). Он имеет следующие атрибуты:

url

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

errcode

Код ошибки.

errmsg

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

headers

Словарь (dict), содержащий заголовки HTTP/HTTPS-запроса, вызвавшего ошибку.

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

import xmlrpc.client

# Создание ServerProxy с URI, который не отвечает на XMLRPC-запросы
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)

21.26.6. Объекты MultiCallMultiCall Objects

Объект MultiCall позволяет инкапсулировать несколько вызовов к удалённому серверу в один запрос 1.

class xmlrpc.client.MultiCall(server)

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

# Простой сервер с простыми арифметическими функциями
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))

21.26.7. Вспомогательные функцииConvenience Functions

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

Преобразует params в запрос XML-RPC. Или в ответ, если methodresponse истинно. params может быть кортежем аргументов или экземпляром Fault класса исключения. Если methodresponse истинно, только одно значение может быть возвращено, то есть params должен иметь длину 1. encoding, если указан, это кодировка для генерируемого XML; по умолчанию UTF-8. Значение None Python не может использоваться в стандартном XML-RPC; чтобы разрешить его использование через расширение, передайте истинное значение для 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; по умолчанию этот флаг выключен.

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

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

21.26.8. Пример использования клиентаExample of Client Usage

# простая тестовая программа (из спецификации 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))

21.26.9. Пример использования клиента и сервераExample of Client and Server Usage

См. пример SimpleXMLRPCServer.

Сноски

1

Этот подход был впервые представлен в обсуждении на xmlrpc.com.