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


XML-RPC — это метод удалённого вызова процедур, который использует 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, *, headers=(), context=None)

Экземпляр ServerProxy — это объект, который управляет связью с удалённым сервером XML-RPC. Обязательный первый аргумент — это URI (унифицированный индикатор ресурсов), и обычно это URL-адрес сервера. Необязательный второй аргумент — это экземпляр транспортной фабрики; по умолчанию это внутренний экземпляр SafeTransport для https: URL- адресов и внутренний экземпляр HTTP Transport в противном случае. Необязательный третий аргумент — это кодировка, по умолчанию UTF-8. Необязательный четвертый аргумент — это флаг отладки.

Следующие параметры управляют использованием возвращенного экземпляра прокси. Если allow_none истинно, константа Python None будет преобразована в XML; по умолчанию для None вызывается TypeError. Это широко используемое расширение спецификации XML-RPC, но поддерживается не всеми клиентами и серверами; описание см. на сайте. Флаг use_builtin_types можно использовать, чтобы значения даты/времени представлялись как объекты datetime.datetime, а двоичные данные — как объекты bytes; данный флаг по умолчанию является ложным. В вызовы могут передаваться объекты datetime.datetime, bytes и bytearray. Параметр headers — это необязательная последовательность HTTP заголовков для отправки с каждым запросом, выраженная в виде последовательности из двух кортежей, представляющих имя и значение заголовка. (например, [(«Имя-заголовка», «значение»)]). Устаревший флаг 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. Вам нужно использовать это только в том случае, если удалённый сервер требует пользователя и пароль для обычной аутентификации. Если указан URL-адрес 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 имеет истинное значение.
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. См. документацию по Apache.

См.также

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

Объекты 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 поддерживают протокол менеджера контекста для закрытия базового транспорта.

Далее следует рабочий пример. Код сервера:

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

Объекты даты и времени

class xmlrpc.client.DateTime

Данный класс может быть инициализирован секундами с начала эпохи, кортежем времени, строкой времени/даты ISO 8601 или экземпляром datetime.datetime. Он имеет следующие методы, поддерживаемые в основном для внутреннего использования кодом маршалинга/демаршалинга:

decode(string)

Принять строку в качестве нового значения времени экземпляра.

encode(out)

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

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

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

class xmlrpc.client.Binary

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

data

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

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

decode(bytes)

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

encode(out)

Записать кодировку XML-RPC base 64 этого двоичного элемента в объект потока 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)

Объекты Fault

class xmlrpc.client.Fault

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

faultCode

Строка, указывающая тип неисправности.

faultString

Строка, содержащая диагностическое сообщение, связанное с ошибкой.

В следующем примере мы собираемся намеренно вызвать Fault, возвращая объект сложного типа. Код сервера:

from xmlrpc.server import SimpleXMLRPCServer

# Произойдет ошибка Marshalling, потому что мы возвращаем комплексное число
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

# создать ServerProxy с URI, который не отвечает на XMLRPC запросы
proxy = xmlrpc.client.ServerProxy("https://digitology.tech/")

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 — конечная цель вызова. Вызовы могут быть сделаны к объекту результата, но они немедленно вернут 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

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

Пример использования клиента

# Простая программа тестирования (из спецификации XML-RPC)
from xmlrpc.client import ServerProxy, Error

# server = ServerProxy("http://localhost:8000") # локальный сервер
with ServerProxy("https://digitology.tech") 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('https://digitology.tech', transport=transport)
print(server.examples.getStateName(41))

Пример использования клиента и сервера

См. SimpleXMLRPCServer Пример.

Сноски

[1]Впервые данный подход был представлен в обсуждении на сайте xmlrpc.