Domanda

Ho una classe che desidero esporre come servizio remoto usando Pythons SimpleXMLRPCServer. L'avvio del server è simile al seguente:

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

service = Service()

server.register_instance(service)
server.serve_forever()

Ho quindi una classe ServiceRemote che assomiglia a questa:

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)

Quindi tutte le chiamate sull'oggetto ServiceRemote verranno inoltrate a xmlrpclib.Server, che quindi lo inoltra al server remoto. Il problema è un metodo nel servizio che prende il nome varargs:

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

Il decoratore @useDb esegue il wrapping della funzione, creando il db prima della chiamata e aprendolo, quindi chiudendolo al termine della chiamata prima di restituire il risultato.

Quando chiamo questo metodo, visualizzo l'errore " call () ho ricevuto un argomento inaspettato "name" " ;. Quindi, è possibile chiamare i metodi prendendo da remoto argomenti denominati variabili? O dovrò creare una sostituzione per ogni variazione di metodo di cui ho bisogno.


Grazie per le risposte. Ho modificato un po 'il mio codice, quindi la domanda non è più un problema. Tuttavia ora lo so per riferimento futuro se effettivamente dovessi implementare argomenti posizionali e supportare l'invocazione remota. Penso che una combinazione di approcci Thomas e Praptaks sarebbe buona. Trasformare i kwarg in argomenti posizionali sul client tramite xmlrpclient e disporre di un wrapper sul lato server dei metodi per decomprimere gli argomenti posizionali.

È stato utile?

Soluzione

Non puoi farlo con xmlrpc semplice poiché non ha nozioni di argomenti di parole chiave. Tuttavia, è possibile sovrapporre questo come protocollo in cima a xmlrpc che passerebbe sempre un elenco come primo argomento e un dizionario come secondo, quindi fornire il codice di supporto adeguato in modo che diventi trasparente per l'uso, esempio di seguito:

Server

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

Altri suggerimenti

XML-RPC non ha davvero un concetto di "argomenti di parole chiave", quindi xmlrpclib non cerca di supportarli. Dovresti scegliere una convenzione, quindi modificare xmlrpclib._Method per accettare gli argomenti delle parole chiave e trasmetterli utilizzando quella convenzione.

Ad esempio, lavoravo con un server XML-RPC che passava argomenti di parole chiave come due argomenti, "-KEYWORD" seguito dall'argomento effettivo, in un elenco semplice. Non ho più accesso al codice che ho scritto per accedere a quel server XML-RPC da Python, ma era abbastanza semplice, sulla falsariga di:

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

Usa il monkeypatching perché è di gran lunga il metodo più semplice per farlo, a causa di alcuni usi ingombranti dei globuli di modulo e degli attributi alterati del nome (__quest, ad esempio) nella classe ServerProxy.

Per quanto ne so, il protocollo sottostante non supporta varargs nominati (o qualsiasi args nominato per quella materia). Per risolvere il problema, creare un wrapper che prenda i ** kwarg e lo passi come un normale dizionario al metodo che si desidera chiamare. Qualcosa del genere

Lato server:

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

Sul lato 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: il codice mostra l'idea generale, puoi farlo un po 'più pulito (ad esempio scrivendo un decoratore per farlo).

Come ha detto Thomas Wouters, XML-RPC non ha argomenti di parole chiave. Solo l'ordine degli argomenti è importante per quanto riguarda il protocollo e possono essere chiamati qualsiasi cosa in XML: arg0, arg1, arg2 va benissimo, così come formaggio, caramelle e pancetta per gli stessi argomenti.

Forse dovresti semplicemente ripensare il tuo uso del protocollo? Usare qualcosa come SOAP documento / letterale sarebbe molto meglio di una soluzione alternativa come quelle presentate in altre risposte qui. Naturalmente, questo potrebbe non essere fattibile.

Usando i consigli di cui sopra, ho creato del codice funzionante.

wrapper del metodo server:

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

Configurazione client (eseguire una volta):

_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

L'ho provato e supporta il metodo con argomenti fissi, posizionali e di parole chiave.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top