"xmlrpc.client" — Client XML-RPC
********************************

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

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

XML-RPC est une méthode d'appel de procédure distante qui utilise du
XML transmis via HTTP(S) comme transport. Un client peut ainsi appeler
des méthodes avec des paramètres sur un serveur distant (le serveur
est nommé par un URI) et récupérer des données structurées. Ce module
prend en charge l'écriture de code client XML-RPC ; il gère la
traduction entre les objets Python et le XML sur le réseau.

Avertissement:

  The "xmlrpc.client" module is not secure against maliciously
  constructed data.  If you need to parse untrusted or unauthenticated
  data, see XML security.

Modifié dans la version 3.5: For HTTPS URIs, "xmlrpc.client" now
performs all the necessary certificate and hostname checks by default.

Disponibilité: not WASI.

Ce module ne fonctionne pas, ou n'est pas disponible en WebAssembly.
Voir Plateformes WebAssembly pour plus d'informations.

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

   Une instance "ServerProxy" est un objet qui gère la communication
   avec un serveur XML-RPC distant. Le premier argument, requis, est
   un URI (*Uniform Resource Identifier*, litt. identifiant uniforme
   de ressource), et est normalement l'URL du serveur. Le deuxième
   argument, facultatif, est une instance de fabrique de transport ;
   par défaut, il s'agit d'une instance de "SafeTransport" pour les
   URL HTTPS sinon d'une instance de "Transport". Le troisième
   argument , facultatif, est un encodage, par défaut UTF-8. Le
   quatrième argument, facultatif, est un indicateur de débogage.

   Les paramètres suivants régissent l'utilisation de l'instance de
   mandataire renvoyée. Si *allow_none* est vrai, la constante Python
   "None" est traduite en XML ; le comportement par défaut est que
   "None" lève une "TypeError". Il s'agit d'une extension de la
   spécification XML-RPC couramment utilisée, mais elle n'est pas
   prise en charge par tous les clients et serveurs ; voir
   http://ontosys.com/xml-rpc/extensions.php. L'indicateur
   *use_builtin_types* peut être utilisé pour que les dates et heures
   soient présentées comme des objets "datetime.datetime" et que les
   données binaires soient présentées comme des objets "bytes" ; cet
   indicateur est faux par défaut. Les objets "datetime.datetime",
   "bytes" et "bytearray" peuvent être transmis aux appels. Le
   paramètre *headers* est une séquence facultative d'en-têtes HTTP à
   envoyer avec chaque requête, exprimée sous la forme d'une séquence
   de paires représentant le nom et la valeur de l'en-tête (par
   exemple "[('Header-Name', 'value')]"). Si une l'URL utilise HTTPS,
   *context* peut-être fourni sous forme d'une "ssl.SSLContext" pour
   configurer les paramètres SSL de la connexion sous-jacente.
   L'indicateur obsolète *use_datetime* est similaire à
   *use_builtin_types* mais il s'applique uniquement aux valeurs de
   date et heure.

   Modifié dans la version 3.3: l'indicateur *use_builtin_types* a été
   ajouté.

   Modifié dans la version 3.8: le paramètre *headers* a été ajouté.

   Les transports HTTP et HTTPS prennent en charge l'extension de
   syntaxe URL pour l'authentification HTTP basique :
   "http://user:pass@host:port/path". La partie "user:pass" est codée
   en base64 en tant qu'en-tête HTTP « *Authorization* » et envoyée au
   serveur distant lors de l'appel d'une méthode XML-RPC. Vous ne
   devez l'utiliser que si le serveur distant nécessite un utilisateur
   et un mot de passe (*Basic Authentication*).

   L'instance renvoyée est un objet mandataire avec des méthodes qui
   peuvent être utilisées pour effectuer les appels RPC correspondants
   sur le serveur distant. Si le serveur distant prend en charge l'API
   d'introspection, le mandataire peut également être utilisé pour
   interroger le serveur distant sur les méthodes qu'il prend en
   charge (découverte de services) et récupérer d'autres métadonnées
   associées au serveur.

   Les types conformes (qui peuvent être sérialisés via XML) sont les
   éléments suivants (et, sauf indication contraire, sont du même type
   Python une fois désérialisés) :

   +------------------------+---------------------------------------------------------+
   | Type XML-RPC           | Type Python                                             |
   |========================|=========================================================|
   | "boolean"              | "bool"                                                  |
   +------------------------+---------------------------------------------------------+
   | "int", "i1", "i2",     | "int" compris entre -2147483648 et 2147483647. Les      |
   | "i4", "i8" ou          | valeurs sont marquées avec la balise "<int>".           |
   | "biginteger"           |                                                         |
   +------------------------+---------------------------------------------------------+
   | "double" ou "float"    | "float". Les valeurs reçoivent la balise "<double>".    |
   +------------------------+---------------------------------------------------------+
   | "string"               | "str"                                                   |
   +------------------------+---------------------------------------------------------+
   | "array"                | "liste" ou "n-uplet" contenant des éléments conformes.  |
   |                        | Les tableaux sont renvoyés sous forme de "listes".      |
   +------------------------+---------------------------------------------------------+
   | "struct"               | "dict". Les clés doivent être des chaînes, les valeurs  |
   |                        | peuvent être de n'importe quel type conforme. Des       |
   |                        | instances de classes définies par l'utilisateur peuvent |
   |                        | être transmises ; seul leur attribut "__dict__" est     |
   |                        | transmis.                                               |
   +------------------------+---------------------------------------------------------+
   | "dateTime.iso8601"     | "DateTime" ou "datetime.datetime". Le type renvoyé      |
   |                        | dépend des valeurs des indicateurs *use_builtin_types*  |
   |                        | et *use_datetime*.                                      |
   +------------------------+---------------------------------------------------------+
   | "base64"               | "Binary", "bytes" ou "bytearray". Le type renvoyé       |
   |                        | dépend de la valeur de l'indicateur                     |
   |                        | *use_builtin_types*.                                    |
   +------------------------+---------------------------------------------------------+
   | "nil"                  | La constante "None". Son utilisation n'est autorisée    |
   |                        | que si *allow_none* est vrai.                           |
   +------------------------+---------------------------------------------------------+
   | "bigdecimal"           | "decimal.Decimal". Type renvoyé uniquement.             |
   +------------------------+---------------------------------------------------------+

   Il s'agit de l'ensemble complet des types de données pris en charge
   par XML-RPC. Les appels de méthode peuvent également lever
   l'exception "Fault", utilisée pour signaler les erreurs du serveur
   XML-RPC, ou l'exception "ProtocolError" utilisée pour signaler une
   erreur dans la couche de transport HTTP/HTTPS. "Fault" et
   "ProtocolError" dérivent d'une exception mère appelée "Error".
   Notez que le module "xmlrpc.client" ne sérialise actuellement pas
   les instances de sous-classes de types natifs.

   Lors de la sérialisation de chaînes, les caractères spéciaux à XML
   tels que "<", ">" et "&" sont automatiquement échappés. Cependant,
   il est de la responsabilité de l'appelant de s'assurer que la
   chaîne ne contient pas de caractères non autorisés en XML, tels que
   les caractères de contrôle avec des valeurs ASCII comprises entre 0
   et 31 (sauf, bien sûr, la tabulation, la nouvelle ligne et le
   retour chariot) ; si vous ne le faites pas, vous obtiendrez une
   requête XML-RPC qui n'est pas du XML valide. Si vous devez
   transmettre des octets arbitraires via XML-RPC, utilisez les
   classes "bytes" ou "bytearray" ou la classe de surcouche "Binary"
   décrite ci-dessous.

   "Server" est conservé comme alias pour "ServerProxy" pour des
   raisons de compatibilité descendante. Le nouveau code doit utiliser
   "ServerProxy".

   Modifié dans la version 3.5: ajout de l'argument *context*.

   Modifié dans la version 3.6: ajout du support des balises de type
   avec préfixes (par exemple "ex:nil"). Ajout de la prise en charge
   des types supplémentaires utilisés par l'implémentation Apache XML-
   RPC pour les valeurs numériques : "i1", "i2", "i8", "biginteger",
   "float" et "bigdecimal". Voir
   https://ws.apache.org/xmlrpc/types.html pour une description.

Voir aussi:

  Guide pratique XML-RPC
     Une bonne description du fonctionnement de XML-RPC et du logiciel
     client pour plusieurs langages. Contient à peu près tout ce qu'un
     développeur de client XML-RPC doit savoir.

  Introspection de XML-RPC
     Décrit l'extension du protocole XML-RPC pour l'introspection.

  Spécification XML-RPC
     La spécification officielle.


Objets *ServerProxy*
====================

Un "ServerProxy" possède une méthode correspondant à chaque procédure
distante acceptée par le serveur XML-RPC. L'appel de la méthode
effectue un appel distant (*RPC*) en fonction du nom et de la
signature des arguments (le même nom de méthode pouvant être surchargé
avec plusieurs signatures d'arguments). L'appel termine soit en
renvoyant une valeur dans un type conforme, soit en levant une "Fault"
ou une "ProtocolError" indiquant une erreur.

Les serveurs qui prennent en charge l'API d'introspection XML prennent
en charge certaines méthodes courantes regroupées sous l'attribut
réservé "system" :

ServerProxy.system.listMethods()

   Cette méthode renvoie une liste de chaînes, une pour chaque méthode
   (non système) prise en charge par le serveur XML-RPC.

ServerProxy.system.methodSignature(name)

   Cette méthode prend un paramètre, le nom d'une méthode implémentée
   par le serveur XML-RPC. Elle renvoie une liste de signatures
   possibles pour cette méthode. Une signature est une liste de types.
   Le premier de ces types est le type de retour de la méthode, les
   autres sont des paramètres.

   Étant donné que plusieurs signatures (c'est-à-dire surcharges) sont
   autorisées, cette méthode renvoie une liste de signatures.

   Les signatures elles-mêmes sont limitées aux paramètres de niveau
   supérieur attendus par une méthode. Par exemple, si une méthode
   attend un tableau de structures comme paramètre et qu'elle renvoie
   une chaîne, sa signature est simplement "[str, list]". Si elle
   attend trois entiers et renvoie une chaîne, sa signature est "[str,
   int, int, int]".

   Si aucune signature n'est définie pour la méthode, le type de la
   valeur renvoyée est autre chose qu'une liste.

ServerProxy.system.methodHelp(name)

   Cette méthode prend un paramètre, le nom d'une méthode implémentée
   par le serveur XML-RPC. Elle renvoie une chaîne de documentation
   décrivant l'utilisation de cette méthode. Si aucune chaîne de ce
   type n'est disponible, une chaîne vide est renvoyée. La chaîne de
   documentation peut contenir un balisage HTML.

Modifié dans la version 3.5: les instances de "ServerProxy" prennent
en charge le protocole *context manager* pour fermer le transport
sous-jacent.

Voici un exemple concret. Le code du serveur :

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

Le code client correspondant au serveur précédent :

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


Objets "DateTime"
=================

class xmlrpc.client.DateTime

   Cette classe peut être initialisée avec les secondes écoulées
   depuis *epoch*, un *n*-uplet temporel (semblable à un
   "time.struct_time"), une chaîne au format ISO 8601 ou une instance
   de "datetime.datetime". Elle dispose des méthodes suivantes, prises
   en charge principalement pour un usage interne par le code de
   sérialisation-désérialisation :

   decode(string)

      Accepte une chaîne comme nouvelle valeur temporelle de
      l'instance.

   encode(out)

      Sérialise ce "DateTime" au format attendu par XML-RPC et l'écrit
      dans l'objet de flux *out*.

   Elle prend également en charge certains opérateurs natifs de Python
   via les "méthodes de comparaison riches" et "__repr__()".

Voici un exemple concret. Le code du serveur :

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

Le code client correspondant au serveur précédent :

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


Objets binaires
===============

class xmlrpc.client.Binary

   Cette classe peut être initialisée à partir d'instances de "bytes"
   (qui peuvent inclure des NUL). L'accès principal au contenu d'un
   objet "Binary" est fourni par un attribut :

   data

      Données binaires encapsulées par l'instance "Binary". Les
      données sont fournies sous forme d'objet "bytes".

   Les objets "Binary" ont les méthodes suivantes, prises en charge
   principalement pour un usage interne par le code de sérialisation-
   désérialisation :

   decode(bytes)

      Accepte un objet "bytes" encodant de la donnée en base64 et le
      décode comme les nouvelles données de l'instance.

   encode(out)

      Sérialise cette instance en base 64 tel qu'attendu par XML-RPC
      dans l'objet de flux *out*.

      Les données encodées sont découpées en lignes de 76 caractères
      conformément à la **RFC 2045 section 6.8**, qui était la
      spécification base64 standard *de facto* lorsque la
      spécification XML-RPC a été écrite.

   Elle prend également en charge certains opérateurs natifs de Python
   via les méthodes "__eq__()" et "__ne__()".

Exemple d'utilisation des objets binaires. Nous allons transférer une
image via 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()

Le client récupère l'image et l'enregistre dans un fichier :

   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)


Objets *Fault*
==============

class xmlrpc.client.Fault

   Un objet "Fault" encapsule le contenu d'une balise d'erreur XML-
   RPC. Les objets d'erreurs ont les attributs suivants :

   faultCode

      Entier indiquant le type d'erreur.

   faultString

      Chaîne contenant un message de diagnostic associé à l'erreur.

Dans l'exemple suivant, nous allons provoquer intentionnellement une
"Fault" en renvoyant un objet de type complexe. Le code du serveur :

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

Le code client correspondant au serveur précédent :

   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)


Objets *ProtocolError*
======================

class xmlrpc.client.ProtocolError

   Un objet "ProtocolError" décrit une erreur de protocole dans la
   couche de transport sous-jacente (telle qu'une erreur 404 « *not
   found* » si le serveur nommé par l'URI n'existe pas). Il possède
   les attributs suivants :

   url

      URI ou URL qui a déclenché l'erreur.

   errcode

      Code d'erreur.

   errmsg

      Message d'erreur ou chaîne de diagnostic.

   headers

      Dictionnaire contenant les en-têtes de la requête HTTP/HTTPS qui
      a déclenché l'erreur.

Dans l'exemple suivant, nous allons intentionnellement provoquer une
"ProtocolError" en fournissant un URI non valide :

   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)


Objets "MultiCall"
==================

L'objet "MultiCall" fournit un moyen d'encapsuler plusieurs appels
vers un serveur distant dans une seule requête [1].

class xmlrpc.client.MultiCall(server)

   Crée un objet utilisé pour regrouper plusieurs appels de méthode.
   *server* est la cible de l'appel. Appeler des méthodes sur une
   instance de "MultiCall" ne fait que stocker le nom de la méthode et
   ses arguments, l'appel renvoie immédiatement "None"  . Appeler
   l'objet lui-même entraîne la transmission de tous les appels
   stockés sous la forme d'une seule requête "system.multicall". Le
   résultat de cet appel est un *générateur* ; itérer sur ce
   générateur donne les résultats individuels.

Voici un exemple d'utilisation de cette classe. Le code du serveur :

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

Le code client correspondant au serveur précédent :

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


Fonctions utilitaires
=====================

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

   Convert *params* into an XML-RPC request, or into a response if
   *methodresponse* is true. *params* can be either a tuple of
   arguments or an instance of the "Fault" exception class.  If
   *methodresponse* is true, only a single value can be returned,
   meaning that *params* must be of length 1. *encoding*, if supplied,
   is the encoding to use in the generated XML; the default is UTF-8.
   Python's "None" value cannot be used in standard XML-RPC; to allow
   using it via an extension,  provide a true value for *allow_none*.

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

   Convertit une requête ou une réponse XML-RPC en une paire "(params,
   methodname)". *params* est un *n*-uplet d'arguments ; *methodname*
   est une chaîne, ou "None" si aucun nom de méthode n'est présent
   dans le paquet. Si le paquet XML-RPC représente une erreur, cette
   fonction lève une exception "Fault". L'indicateur
   *use_builtin_types* peut être utilisé pour que les valeurs
   temporelles soient converties en objets "datetime.datetime" et que
   les données binaires soient présentées comme des objets "bytes" ;
   cet indicateur est faux par défaut.

   L'indicateur obsolète *use_datetime* est similaire à
   *use_builtin_types* mais il s'applique uniquement aux valeurs
   temporelles.

   Modifié dans la version 3.3: l'indicateur *use_builtin_types* a été
   ajouté.


Exemple d'utilisation du client
===============================

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

Pour accéder à un serveur XML-RPC via un mandataire HTTP, vous devez
définir un transport personnalisé. L'exemple suivant montre comment
faire :

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


Exemple d'utilisation du client et du serveur
=============================================

Voir Exemple SimpleXMLRPCServer.

-[ Notes ]-

[1] Cette approche a été présentée pour la première fois dans une
    discussion sur xmlrpc.com .
