Содержание страницы
21.26. xmlrpc.client – доступ к XML-RPC клиенту¶xmlrpc.client – XML-RPC client access
Исходный код: Lib/xmlrpc/client.py
XML-RPC – это метод удаленного вызова процедур, использующий XML, передаваемый через HTTP в качестве tранспорта. С его помощью клиент может вызывать методы с параметрами на удаленном сервере (сервер задается URI) и получать структурированные данные. Этот модуль поддерживает написание клиентского кода XML-RPC; он обрабатывает все детали преобразования между совместимыми объектами Python и XML при передаче.
Предупреждение
Модуль xmlrpc.client не защищен от вредоносных данных. Если вам нужно разбирать непроверенные или неаутентифицированные данные, см. уязвимости XML.
Изменено в версии 3.4.3: Для 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 (универсальный идентификатор ресурса), обычно это URL сервера. Второй необязательный аргумент – фабрика транспорта; по умолчанию это внутренний экземпляр SafeTransport для URL с https: и внутренний экземпляр HTTP Transport в противном случае. Третий необязательный аргумент – кодировка, по умолчанию UTF-8. Четвертый необязательный аргумент – флаг отладки. Если allow_none истинно, константа Python None будет преобразована в XML; по умолчанию None вызывает исключение TypeError. Это часто используемое расширение спецификации XML-RPC, но оно поддерживается не всеми клиентами и серверами; описание см. на http://ontosys.com/xml-rpc/extensions.php. Флаг use_builtin_types может использоваться для представления значений даты/времени как объектов datetime.datetime и двоичных данных как объектов bytes; по умолчанию этот флаг ложен. Объекты datetime.datetime и bytes могут передаваться в вызовы.
Устаревший флаг 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 интроспекции, прокси также можно использовать для запроса методов, которые он поддерживает (обнаружение служб), и получения других метаданных, связанных с сервером.
Методы экземпляра ServerProxy принимают в качестве аргументов основные типы и объекты Python и возвращают основные типы и классы Python. Типы, которые являются совместимыми (т.е. могут быть преобразованы через XML), включают следующие (и, если не указано иное, они десериализуются в тот же тип Python):
Имя Значение логическое значение Константы True и False целые числа Передаются напрямую с плавающей запятой числа Передаются напрямую строки Передаются напрямую массивы Любой тип последовательности Python, содержащий совместимые элементы. Массивы возвращаются как списки. структуры Словарь Python. Ключи должны быть строками, значения – любым совместимым типом. Можно передавать объекты пользовательских классов; передается только их атрибут __dict__. даты В секундах с начала эпохи. Передайте экземпляр класса DateTime или экземпляр datetime.datetime. двоичные данные Передайте экземпляр класса-обертки Binary или экземпляр bytes. Это полный набор типов данных, поддерживаемых XML-RPC. Вызовы методов также могут вызывать специальный экземпляр Fault, используемый для сигнализации ошибок XML-RPC сервера, или ProtocolError, используемый для сигнализации ошибки в транспортном слое HTTP/HTTPS. Оба Fault и ProtocolError наследуются от базового класса Error. Обратите внимание, что модуль xmlrpc.client в настоящее время не маршалирует экземпляры подклассов встроенных типов.
При передаче строк символы, специальные для XML, такие как <, > и &, будут автоматически экранированы. Однако вызывающая сторона должна убедиться, что строка не содержит символов, запрещенных в XML, например управляющих символов с кодами ASCII от 0 до 31 (за исключением, конечно, табуляции, новой строки и возврата каретки); невыполнение этого требования приведет к XML-RPC запросу, который не является правильно оформленным XML. Если необходимо передать произвольные байты через XML-RPC, используйте класс bytes или класс-обертку Binary, описанный ниже.
Server сохранен как псевдоним ServerProxy для обратной совместимости. В новом коде следует использовать ServerProxy.
Изменено в версии 3.4.3: Добавлен аргумент context.
См. также
- Руководство по XML-RPC
- Хорошее описание работы XML-RPC и клиентского ПО на нескольких языках. Содержит практически всё, что нужно знать разработчику XML-RPC клиента.
- Интроспекция XML-RPC
- Описывает расширение протокола XML-RPC для интроспекции.
- Спецификация XML-RPC
- Официальная спецификация.
- Неофициальные исправления XML-RPC
- «Неофициальные исправления» Фредрика Лунда, предназначенные для разъяснения некоторых деталей спецификации XML-RPC, а также для указания на «лучшие практики» при разработке собственных реализаций XML-RPC.
21.26.1. Объекты ServerProxy¶ServerProxy Objects
Экземпляр ServerProxy имеет метод, соответствующий каждому удаленному вызову процедуры, принимаемому XML-RPC сервером. Вызов метода выполняет RPC, диспетчеризуемый по имени и сигнатуре аргументов (например, одно и то же имя метода может быть перегружено несколькими сигнатурами). RPC завершается возвратом значения, которое может быть либо возвращенными данными в совместимом типе, либо объектом Fault или ProtocolError, указывающим на ошибку.
Серверы, поддерживающие XML introspection API, поддерживают некоторые общие методы, сгруппированные в зарезервированном атрибуте 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-разметку.
Далее следует работающий пример. Код сервера:
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
proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
print("3 is even: %s" % str(proxy.is_even(3)))
print("100 is even: %s" % str(proxy.is_even(100)))
21.26.2. Объекты DateTime¶DateTime Objects
Этот класс может быть инициализирован секундами с начала эпохи, временным кортежем, строкой времени/даты в формате ISO 8601 или экземпляром datetime.datetime. Он имеет следующие методы, поддерживаемые в основном для внутреннего использования кодом маршалинга/демаршалинга:
- DateTime.decode(string)¶
Принимает строку в качестве нового значения времени экземпляра.
- DateTime.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"))
21.26.3. Объекты Binary¶Binary Objects
Этот класс может быть инициализирован из данных bytes (которые могут содержать NUL). Основной доступ к содержимому объекта Binary предоставляется атрибутом:
- Binary.data¶
Двоичные данные, инкапсулированные экземпляром Binary. Данные предоставляются в виде объекта bytes.
Объекты Binary имеют следующие методы, поддерживаемые в основном для внутреннего использования кодом маршалинга/демаршалинга:
- Binary.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. Объекты Fault¶Fault Objects
Объект Fault инкапсулирует содержимое тега ошибки XML-RPC. Объекты Fault имеют следующие атрибуты:
- Fault.faultCode¶
Строка, указывающая тип ошибки.
- Fault.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. Объекты ProtocolError¶ProtocolError Objects
Объект ProtocolError описывает ошибку протокола на уровне нижележащего транспорта (например, ошибка 404 «не найдено», если сервер, указанный в URI, не существует). Он имеет следующие атрибуты:
- ProtocolError.url¶
URI или URL, вызвавший ошибку.
- ProtocolError.errcode¶
Код ошибки.
- ProtocolError.errmsg¶
Сообщение об ошибке или диагностическая строка.
- ProtocolError.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. Объекты MultiCall¶MultiCall Objects
Объект MultiCall предоставляет способ инкапсулировать несколько вызовов удаленного сервера в один запрос [1].
- class xmlrpc.client.MultiCall(server)¶
Create an object used to boxcar method calls. server is the eventual target of the call. Calls can be made to the result object, but they will immediately return None, and only store the call name and parameters in the MultiCall object. Calling the object itself causes all stored calls to be transmitted as a single system.multicall request. The result of this call is a generator; iterating over this generator yields the individual results.
Далее приведён пример использования этого класса. Серверный код:
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; по умолчанию этот флаг равен false.
Устаревший флаг 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") # локальный сервер
server = ServerProxy("http://betty.userland.com")
print(server)
try:
print(server.examples.getStateName(41))
except Error as v:
print("ERROR", v)
Для доступа к XML-RPC серверу через прокси необходимо определить собственный transport. Следующий пример показывает, как это сделать:
import xmlrpc.client, http.client
class ProxiedTransport(xmlrpc.client.Transport):
def set_proxy(self, proxy):
self.proxy = proxy
def make_connection(self, host):
self.realhost = host
h = http.client.HTTP(self.proxy)
return h
def send_request(self, connection, handler, request_body):
connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))
def send_host(self, connection, host):
connection.putheader('Host', self.realhost)
p = ProxiedTransport()
p.set_proxy('proxy-server:8080')
server = xmlrpc.client.Server('http://time.xmlrpc.com/RPC2', transport=p)
print(server.currentTime.getCurrentTime())
21.26.9. Пример использования клиента и сервера¶Example of Client and Server Usage
См. пример SimpleXMLRPCServer.
Сноски
| [1] | Этот подход был впервые представлен в обсуждении на xmlrpc.com. |