"xmlrpc.client" --- acceso cliente XML-RPC
******************************************

**Source code:** Lib/xmlrpc/client.py

======================================================================

XML-RPC es un método de llamada a procedimiento remoto que utiliza XML
pasado a través de HTTP(S) como transporte. Con él, un cliente puede
llamar a métodos con parámetros en un servidor remoto (el servidor es
nombrado por un URI) y recuperar datos estructurados. Este módulo
admite la escritura de código de cliente XML-RPC; maneja todos los
detalles de la traducción entre objetos de Python conformes y XML en
el cable.

Advertencia:

  El módulo "xmlrpc.client" no es seguro contra datos construidos
  maliciosamente. Si necesita analizar datos que no son de confianza o
  no autenticados, consulte Vulnerabilidades XML.

Distinto en la versión 3.5: Para HTTPS URI, "xmlrpc.client" ahora
realiza todas las comprobaciones necesarias de certificados y nombres
de host de forma predeterminada.

class xmlrpc.client.ServerProxy(uri, transport=None, encoding=None, verbose=False, allow_none=False, use_datetime=False, use_builtin_types=False, *, headers=(), context=None)

   Una "ServerProxy" instancia un objeto que gestiona la comunicación
   con un servidor XML-RPC remoto. El primer argumento requerido es un
   URI (*Uniform Resource Indicator*) y normalmente será la URL del
   servidor. El segundo argumento opcional es una instancia de fábrica
   de transporte; de forma predeterminada, es una instancia interna
   "SafeTransport" para https: URL y una instancia interna HTTP
   "Transport" en caso contrario. El tercer argumento opcional es una
   codificación, por defecto UTF-8. El cuarto argumento opcional es un
   indicador de depuración.

   Los siguientes parámetros rigen el uso de la instancia de proxy
   retornada. Si *allow_none* es verdadero, la constante de Python
   "None" se traducirá a XML; el comportamiento predeterminado es que
   "None" genere un "TypeError". Esta es una extensión de uso común
   para la especificación XML-RPC, pero no todos los clientes y
   servidores la admiten; ver *http://ontosys.com/xml-
   rpc/extensions.php
   <https://web.archive.org/web/20130120074804/http://ontosys.com/xml-
   rpc/extensions.php>* _ para una descripción. La flag
   *use_builtin_types* puede usarse para hacer que los valores de
   fecha/hora se presenten como : clase: objetos *datetime.datetime* y
   datos binarios que se presenten como objetos "datetime.datetime" y
   los datos binarios pueden ser presentados como objetos "bytes";
   esta flag es falsa por defecto. Los objetos "datetime.datetime",
   "bytes" y "bytearray" se pueden pasar a las llamadas. El parámetro
   *header* es una secuencia opcional de encabezados HTTP para enviar
   con cada solicitud, expresada como una secuencia de 2 tuplas que
   representan el nombre y el valor del encabezado. (por ejemplo,
   *[('Header-Name', 'value')]*). La flag obsoleta *use_datetime* es
   similar a *use_builtin_types* pero solo se aplica a los valores de
   fecha/hora.

Distinto en la versión 3.3: La flag *use_builtin_types* fue añadida.

Distinto en la versión 3.8: El parámetro *headers* fue añadida.Tanto
los transportes HTTP como HTTPS admiten la extensión de sintaxis de
URL para la autenticación básica
HTTP:"http://user:pass@host:port/path". La parte "user:pass" se
codificará en base64 como un encabezado HTTP de 'Authorization' y se
enviará al servidor remoto como parte del proceso de conexión al
invocar un método XML-RPC. Solo necesita usar esto si el servidor
remoto requiere un usuario y contraseña de autenticación básica. Si se
proporciona una URL HTTPS, el *context* puede ser "ssl.SSLContext" y
configura la configuración SSL de la conexión HTTPS subyacente.La
instancia retornada es un objeto proxy con métodos que se pueden
utilizar para invocar las correspondientes llamadas RPC en el servidor
remoto. Si el servidor remoto admite la API de introspección, el proxy
también se puede utilizar para consultar al servidor remoto los
métodos que admite (descubrimiento de servicios) y recuperar otros
metadatos asociados al servidor.Los tipos que son conformes (por
ejemplo, que se pueden clasificar a través de XML) incluyen lo
siguiente (y, excepto donde se indique, no se clasifican como el mismo
tipo de Python):

+------------------------+---------------------------------------------------------+
| Tipo XML-RPC           | Tipo de Python                                          |
|========================|=========================================================|
| "boolean"              | "bool"                                                  |
+------------------------+---------------------------------------------------------+
| "int", "i1", "i2",     | "int" en el rango de -2147483648 a 2147483647. Los      |
| "i4", "i8" or          | valores obtienen la etiqueta "<int>" .                  |
| "biginteger"           |                                                         |
+------------------------+---------------------------------------------------------+
| "double" o "float"     | "float".  Los valores obtienen la etiqueta "<double>".  |
+------------------------+---------------------------------------------------------+
| "string"               | "str"                                                   |
+------------------------+---------------------------------------------------------+
| "array"                | "list" o "tuple" que contiene elementos determinados.   |
|                        | Las matrices se retornan como "lists".                  |
+------------------------+---------------------------------------------------------+
| "struct"               | "dict". Las claves deben ser cadenas de caracteres, los |
|                        | valores pueden ser de cualquier tipo determinado.       |
|                        | Pueden pasarse objetos de clases definidas por el       |
|                        | usuario; sólo se transmite su atributo "__dict__" .     |
+------------------------+---------------------------------------------------------+
| "dateTime.iso8601"     | "DateTime" o "datetime.datetime". El tipo retornado     |
|                        | depende de los valores de los indicadores               |
|                        | *use_builtin_types* y *use_datetime* .                  |
+------------------------+---------------------------------------------------------+
| "base64"               | "Binary", "bytes" o "bytearray". El tipo retornado      |
|                        | depende del valor de la marca *use_builtin_types*.      |
+------------------------+---------------------------------------------------------+
| "nil"                  | La constante "None". Solo se permite pasar si           |
|                        | *allow_none* es verdadero.                              |
+------------------------+---------------------------------------------------------+
| "bigdecimal"           | "decimal.Decimal". Retornado solo el tipo.              |
+------------------------+---------------------------------------------------------+

Este es el conjunto completo de tipos de datos admitidos por XML-RPC.
Las llamadas a métodos también pueden generar una instancia especial
"Fault", que se usa para señalar errores del servidor XML-RPC, o
"ProtocolError" que se usa para señalar un error en la capa de
transporte HTTP/HTTPS. Ambos "Fault" y "ProtocolError" derivan de una
clase base llamada "Error". Tenga en cuenta que el módulo de cliente
xmlrpc actualmente no clasifica instancias de subclases de tipos
integrados.Al pasar cadenas de caracteres, los caracteres especiales
de XML como "<", ">" y "&" se escaparán automáticamente. Sin embargo,
es responsabilidad de la persona que llama asegurarse de que la cadena
de caracteres esté libre de caracteres que no están permitidos en XML,
como los caracteres de control con valores ASCII entre 0 y 31
(excepto, por supuesto, tabulación, nueva línea y retorno de carro);
no hacer esto resultará en una solicitud XML-RPC que no es XML bien
formado. Si tiene que pasar bytes arbitrarios a través de XML-RPC, use
las clases "bytes" o "bytearray" o la clase contenedora "Binary"
descrita a continuación."Server" se conserva como un alias para
"ServerProxy" para compatibilidad con versiones anteriores. El nuevo
código debe usar "ServerProxy".

Distinto en la versión 3.5: Se agregó el argumento *context*.

Distinto en la versión 3.6: Se agregó soporte para etiquetas de tipo
con prefijos (por ejemplo. "ex:nil"). Se agregó soporte para
desagrupar los tipos adicionales utilizados por la implementación
Apache XML-RPC para números: "i1", "i2", "i8", "biginteger", "float" y
"bigdecimal". Consulte http://ws.apache.org/xmlrpc/types.html para
obtener una descripción.

Ver también:

  XML-RPC HOWTO
     Una buena descripción del funcionamiento de XML-RPC y del
     software cliente en varios idiomas. Contiene prácticamente todo
     lo que un desarrollador de cliente XML-RPC necesita saber.

  XML-RPC Introspection
     Describe la extensión del protocolo XML-RPC para la
     introspección.

  XML-RPC Specification
     La especificación oficial.

  Unofficial XML-RPC Errata
     "Las erratas no oficiales de Fredrik Lundh, destinadas a aclarar
     ciertos detalles en la especificación XML-RPC, así como dar
     pistas sobre las 'mejores prácticas' para usar al diseñar sus
     propias implementaciones XML-RPC".


Objetos *ServerProxy*
=====================

La instancia de "ServerProxy" tiene un método correspondiente a cada
llamada de procedimiento remoto aceptada por el servidor XML-RPC.
Llamar al método realiza un RPC, enviado por nombre y firma de
argumento (por ejemplo, el mismo nombre de método puede sobrecargarse
con múltiples firmas de argumento). El RPC finaliza retornando un
valor, que puede ser datos retornados en un tipo conforme o un objeto
"Fault" o "ProtocolError" que indica un error.

Los servidores que admiten la API de introspección XML admiten algunos
métodos comunes agrupados bajo el atributo reservado "system":

ServerProxy.system.listMethods()

   Este método retorna una lista de cadenas, una para cada método (que
   no es del sistema) admitido por el servidor XML-RPC.

ServerProxy.system.methodSignature(name)

   Este método toma un parámetro, el nombre de un método implementado
   por el servidor XML-RPC. Retorna una matriz de posibles firmas para
   este método. Una firma es una variedad de tipos. El primero de
   estos tipos es el tipo de retorno del método, el resto son
   parámetros.

   Debido a que se permiten múltiples firmas (es decir, sobrecarga),
   este método retorna una lista de firmas en lugar de un singleton.

   Las propias firmas están restringidas a los parámetros de nivel
   superior esperados por un método. Por ejemplo, si un método espera
   una matriz de estructuras como parámetro y retorna una cadena de
   caracteres, su firma es simplemente "cadena, matriz". Si espera
   tres enteros y retorna una cadena, su firma es "string, int, int,
   int".

   Si no se define una firma para el método, se retorna un valor que
   no es una matriz. En Python, esto significa que el tipo de valor
   retornado será diferente a una lista.

ServerProxy.system.methodHelp(name)

   Este método toma un parámetro, el nombre de un método implementado
   por el servidor XML-RPC. Retorna una cadena de caracteres de
   documentación que describe el uso de ese método. Si no hay tal
   cadena de caracteres disponible, se retorna una cadena de
   caracteres vacía. La cadena de caracteres de documentación puede
   contener marcado HTML.

Distinto en la versión 3.5: Las instancias de "ServerProxy" admiten el
protocolo *context manager* para cerrar el transporte subyacente.

A continuación se muestra un ejemplo práctico. El código del servidor:

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

El código de cliente para el servidor anterior:

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


Objetos *DateTime*
==================

class xmlrpc.client.DateTime

   Esta clase puede inicializarse con segundos desde la época, una
   tupla de tiempo, una cadena de fecha/hora ISO 8601 o una instancia
   "datetime.datetime". Tiene los siguientes métodos, soportados
   principalmente para uso interno por el código de
   clasificación/eliminación de clasificación:

   decode(string)

      Acepta una cadena de caracteres como el nuevo valor de tiempo de
      la instancia.

   encode(out)

      Escribe la codificación XML-RPC de este elemento "DateTime" en
      el objeto de flujo *out*.

   También es compatible con algunos de los operadores integrados de
   Python a través de una rica comparación y métodos "__repr__()".

A continuación se muestra un ejemplo práctico. El código del servidor:

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

El código de cliente para el servidor anterior:

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


Objetos binarios
================

class xmlrpc.client.Binary

   Esta clase puede inicializarse a partir de datos de bytes (que
   pueden incluir NUL). El acceso principal al contenido de un objeto
   "Binary" lo proporciona un atributo:

   data

      Los datos binarios encapsulados por la instancia "Binary". Los
      datos se proporcionan como un objeto "bytes".

   Los objetos "Binary" tienen los siguientes métodos, soportados
   principalmente para uso interno por el código de
   clasificación/desagrupación:

   decode(bytes)

      Acepta un objeto base64 "bytes" y se descodifica como los nuevos
      datos de la instancia.

   encode(out)

      Escribe la codificación XML-RPC base 64 de este elemento binario
      en el objeto de flujo *out*.

      The encoded data will have newlines every 76 characters as per
      **RFC 2045 section 6.8**, which was the de facto standard base64
      specification when the XML-RPC spec was written.

   También admite algunos de los operadores integrados de Python a
   través de los métodos "__eq__()" and "__ne__()".

Ejemplo de uso de los objetos binarios. Vamos a transferir una imagen
sobre 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()

El cliente obtiene la imagen y la guarda en un archivo:

   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)


Objetos Faults
==============

class xmlrpc.client.Fault

   Un objeto "Fault" encapsula el contenido de una etiqueta de error
   XML-RPC. Los objetos de error tienen los siguientes atributos:

   faultCode

      Una cadena de caracteres que indica el tipo de fallo.

   faultString

      Una cadena de caracteres que contiene un mensaje de diagnóstico
      asociado con el fallo.

En el siguiente ejemplo vamos a causar intencionalmente un "Fault" al
retornar un objeto de tipo complejo. El código del servidor:

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

El código de cliente para el servidor anterior:

   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)


Objetos ProtocolError
=====================

class xmlrpc.client.ProtocolError

   El objeto "ProtocolError" describe un error de protocolo en la capa
   de transporte subyacente (como un error 404 'no encontrado' si el
   servidor nombrado por el URI no existe). Tiene los siguientes
   atributos:

   url

      El URI o URL que provocó el error.

   errcode

      El código de error.

   errmsg

      El mensaje de error o la cadena de caracteres de diagnóstico.

   headers

      Un diccionario que contiene los encabezados de la solicitud
      HTTP/HTTPS que desencadenó el error.

En el siguiente ejemplo, vamos a causar intencionalmente un
"ProtocolError" proporcionando un URI inválido:

   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)


Objectos MultiCall
==================

El objeto "MultiCall" proporciona una forma de encapsular múltiples
llamadas a un servidor remoto en una sola solicitud [1].

class xmlrpc.client.MultiCall(server)

   Crea un objeto usado para llamadas al método boxcar. *server* es el
   objetivo final de la llamada. Se pueden realizar llamadas al objeto
   de resultado, pero retornarán inmediatamente "None" y solo
   almacenarán el nombre y los parámetros de la llamada en el objeto
   "MultiCall". Llamar al objeto en sí hace que todas las llamadas
   almacenadas se transmitan como una única solicitud de
   "system.multicall". El resultado de esta llamada es un *generator*;
   iterar sobre este generador produce los resultados individuales.

A continuación se muestra un ejemplo de uso de esta clase. El código
del servidor:

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

El código de cliente para el servidor anterior:

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


Funciones de Conveniencia
=========================

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

   Convierta *params* en una solicitud XML-RPC. o en una respuesta si
   *methodresponse* es verdadero. *params* puede ser una tupla de
   argumentos o una instancia de la clase de excepción "Fault". Si
   *methodresponse* es verdadero, solo se puede devolver un único
   valor, lo que significa que *params* debe tener una longitud de 1.
   *encoding*, si se proporciona, es la codificación que se utilizará
   en el XML generado; el predeterminado es UTF-8. El valor de Python
   "None" no se puede usar en XML-RPC estándar; para permitir su uso a
   través de una extensión, proporcione un valor verdadero para
   *allow_none*.

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

   Convierte una solicitud o respuesta XML-RPC en objetos Python, un
   "(params, methodname)".  *params* es una tupla de argumento;
   *methodname* es una cadena de caracteres, o "None" si no hay ningún
   nombre de método presente en el paquete. Si el paquete XML-RPC
   representa una condición de falla, esta función generará una
   excepción "Fault". La flag *use_builtin_types* puede usarse para
   hacer que los valores de fecha/hora se presenten como objetos de
   "datetime.datetime" y datos binarios que se presenten como objetos
   de "bytes"; esta flag es falsa por defecto.

   La flag obsoleta *use_datetime* es similar a *use_builtin_types*
   pero esto aplica solo a valores fecha/hora.

   Distinto en la versión 3.3: La flag *use_builtin_types* fue
   añadida.


Ejemplo de Uso de Cliente
=========================

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

Para acceder a un servidor XML-RPC a través de un proxy HTTP, debe
definir un transporte personalizado. El siguiente ejemplo muestra
cómo:

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


Ejemplo de Uso de Cliente y Servidor
====================================

Vea Ejemplo de SimpleXMLRPCServer.

-[ Pie de notas ]-

[1] Este enfoque se presentó por primera vez en una discusión en
    xmlrpc.com.
