Question

Je souhaite exposer une classe en tant que service distant à l'aide de pythons SimpleXMLRPCServer. Le démarrage du serveur ressemble à ceci:

server = SimpleXMLRPCServer((serverSettings.LISTEN_IP,serverSettings.LISTEN_PORT))

service = Service()

server.register_instance(service)
server.serve_forever()

J'ai alors une classe ServiceRemote qui ressemble à ceci:

def __init__(self,ip,port):
    self.rpcClient = xmlrpclib.Server('http://%s:%d' %(ip,port))

def __getattr__(self, name):
    # forward all calls to the rpc client
    return getattr(self.rpcClient, name)

Tous les appels sur l'objet ServiceRemote seront alors transférés vers xmlrpclib.Server, qui les transmettra ensuite au serveur distant. Le problème est une méthode du service qui prend le nom varargs:

@useDb
def select(self, db, fields, **kwargs):
    pass

Le décorateur @useDb encapsule la fonction en créant la base de données avant l'appel et en l'ouvrant, puis en la fermant une fois l'appel terminé avant de renvoyer le résultat.

Lorsque j'appelle cette méthode, l'erreur " call () ()" a reçu un argument de mot clé inattendu, "nom" & ";". Alors, est-il possible d'appeler des méthodes en prenant des arguments nommés variables à distance? Ou devrais-je créer un remplacement pour chaque variante de méthode dont j'ai besoin?

Merci pour les réponses. J'ai modifié mon code un peu pour que la question ne soit plus un problème. Cependant, je le sais maintenant pour référence future si j’ai vraiment besoin d’implémenter des arguments de position et de prendre en charge l’invocation à distance. Je pense qu'une combinaison d'approches de Thomas et de praptak serait bien. Transformer kwargs en arguments de position sur le client via xmlrpclient et disposer d’un wrapper sur les méthodes serveur pour décompresser les arguments de position.

Était-ce utile?

La solution

Vous ne pouvez pas faire cela avec plain xmlrpc car il n’a aucune notion d’argument de mot clé. Cependant, vous pouvez superposer cela en tant que protocole sur xmlrpc qui transmettrait toujours une liste en premier argument et un dictionnaire en tant que second, puis en fournissant le code de support approprié pour qu'il devienne transparent pour votre utilisation, exemple ci-dessous:

Serveur

from SimpleXMLRPCServer import SimpleXMLRPCServer

class Server(object):
    def __init__(self, hostport):
        self.server = SimpleXMLRPCServer(hostport)

    def register_function(self, function, name=None):
        def _function(args, kwargs):
            return function(*args, **kwargs)
        _function.__name__ = function.__name__
        self.server.register_function(_function, name)

    def serve_forever(self):
        self.server.serve_forever()

#example usage
server = Server(('localhost', 8000))
def test(arg1, arg2):
    print 'arg1: %s arg2: %s' % (arg1, arg2)
    return 0
server.register_function(test)
server.serve_forever()

Client

import xmlrpclib

class ServerProxy(object):
    def __init__(self, url):
        self._xmlrpc_server_proxy = xmlrpclib.ServerProxy(url)
    def __getattr__(self, name):
        call_proxy = getattr(self._xmlrpc_server_proxy, name)
        def _call(*args, **kwargs):
            return call_proxy(args, kwargs)
        return _call

#example usage
server = ServerProxy('http://localhost:8000')
server.test(1, 2)
server.test(arg2=2, arg1=1)
server.test(1, arg2=2)
server.test(*[1,2])
server.test(**{'arg1':1, 'arg2':2})

Autres conseils

XML-RPC n'a pas vraiment de concept d '"arguments de mots clés". xmlrpclib n'essaie donc pas de les prendre en charge. Vous devez choisir une convention, puis modifier xmlrpclib._Method pour accepter les arguments de mots clés et les transmettre à l'aide de cette convention.

Par exemple, je travaillais avec un serveur XML-RPC qui transmettait les arguments de mot clé sous forme de deux arguments, "-KEYWORD" suivi du paramètre réel, dans une liste non hiérarchique. Je n'ai plus accès au code que j'ai écrit pour accéder à ce serveur XML-RPC à partir de Python, mais c'était assez simple, à l'instar de:

import xmlrpclib

_orig_Method = xmlrpclib._Method

class KeywordArgMethod(_orig_Method):     
    def __call__(self, *args, **kwargs):
        if args and kwargs:
            raise TypeError, "Can't pass both positional and keyword args"
        args = list(args) 
        for key in kwargs:
            args.append('-%s' % key.upper())
            args.append(kwargs[key])
       return _orig_Method.__call__(self, *args)     

xmlrpclib._Method = KeywordArgMethod

Il utilise monkeypatching parce que c'est de loin la méthode la plus simple, en raison de certaines utilisations maladroites de globaux de module et d'attributs altérés (par exemple, __request) dans la classe ServerProxy.

Autant que je sache, le protocole sous-jacent ne prend pas en charge les varargs nommés (ni aucun argument nommé d'ailleurs). La solution de contournement consiste à créer un wrapper qui prendra le ** kwargs et le transmettra sous forme de dictionnaire ordinaire à la méthode que vous souhaitez appeler. Quelque chose comme ça

Côté serveur:

def select_wrapper(self, db, fields, kwargs):
    """accepts an ordinary dict which can pass through xmlrpc"""
    return select(self,db,fields, **kwargs)

Côté client:

def select(self, db, fields, **kwargs):
    """you can call it with keyword arguments and they will be packed into a dict"""
    return self.rpcClient.select_wrapper(self,db,fields,kwargs)

Disclaimer: le code montre l'idée générale, vous pouvez le faire un peu plus propre (par exemple, en écrivant à un décorateur).

Comme l'a dit Thomas Wouters, XML-RPC n'a pas d'argument de mot clé. En ce qui concerne le protocole, seul l’ordre des arguments est important et on peut les appeler en XML: arg0, arg1, arg2 convient parfaitement, tout comme le fromage, les bonbons et le lard pour les mêmes arguments.

Peut-être devriez-vous simplement repenser votre utilisation du protocole? Utiliser quelque chose comme SOAP document / littéral serait bien meilleur qu’une solution de contournement telle que celles présentées dans d’autres réponses ici. Bien sûr, cela pourrait ne pas être réalisable.

En utilisant les conseils ci-dessus, j'ai créé du code de travail.

Enveloppe de la méthode serveur:

def unwrap_kwargs(func):
    def wrapper(*args, **kwargs):
        print args
        if args and isinstance(args[-1], list) and len(args[-1]) == 2 and "kwargs" == args[-1][0]:
            func(*args[:-1], **args[-1][1])
        else:
            func(*args, **kwargs)
    return wrapper

Configuration du client (faire une fois):

_orig_Method = xmlrpclib._Method

class KeywordArgMethod(_orig_Method):     
    def __call__(self, *args, **kwargs):
        args = list(args) 
        if kwargs:
            args.append(("kwargs", kwargs))
        return _orig_Method.__call__(self, *args)

xmlrpclib._Method = KeywordArgMethod

J'ai testé cela et il supporte la méthode avec des arguments fixes, positionnels et mots clés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top