python - example - xmlrpclib odoo
Usando** kwargs con SimpleXMLRPCServer en python (5)
Como dijo Thomas Wouters, XML-RPC no tiene argumentos de palabra clave. Solo el orden de los argumentos importa en lo que respecta al protocolo y se les puede llamar cualquier cosa en XML: arg0, arg1, arg2 está perfectamente bien, como lo es el queso, el caramelo y el tocino por los mismos argumentos.
Tal vez debería simplemente reconsiderar su uso del protocolo? Usar algo como SOAP documento / literal sería mucho mejor que una solución alternativa como las que se presentan en otras respuestas aquí. Por supuesto, esto puede no ser factible.
Tengo una clase que deseo exponer como un servicio remoto usando pythons SimpleXMLRPCServer. El inicio del servidor se ve así:
server = SimpleXMLRPCServer((serverSettings.LISTEN_IP,serverSettings.LISTEN_PORT))
service = Service()
server.register_instance(service)
server.serve_forever()
Luego tengo una clase ServiceRemote que se ve así:
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)
Por lo tanto, todas las llamadas al objeto ServiceRemote se reenviarán a xmlrpclib.Server, que luego lo reenviará al servidor remoto. El problema es un método en el servicio que toma varargs con nombre:
@useDb
def select(self, db, fields, **kwargs):
pass
El decorador @useDb ajusta la función, crea el db antes de la llamada y lo abre, luego lo cierra después de que se realiza la llamada antes de devolver el resultado.
Cuando llamo a este método, obtengo el error " call () obtuvo un argumento de palabra clave inesperado ''name''". Entonces, ¿es posible llamar a métodos que toman los argumentos nombrados por variables de forma remota? O tendré que crear una anulación para cada variación de método que necesito.
Gracias por las respuestas. Cambié mi código un poco para que la pregunta ya no sea un problema. Sin embargo, ahora sé esto para futuras referencias si de hecho necesito implementar argumentos posicionales y admitir la invocación remota. Creo que una combinación de enfoques de Thomas y praptaks sería buena. Convirtiendo kwargs en args posicionales en el cliente a través de xmlrpclient, y teniendo un contenedor en los métodos del lado del servidor para descomponer los argumentos posicionales.
Hasta donde yo sé, el protocolo subyacente no admite varargs con nombre (o cualquier args con nombre para el caso). La solución para esto es crear un contenedor que tome los ** kwargs y pasarlo como un diccionario común al método que desea llamar. Algo como esto
Lado del servidor:
def select_wrapper(self, db, fields, kwargs):
"""accepts an ordinary dict which can pass through xmlrpc"""
return select(self,db,fields, **kwargs)
En el lado del cliente:
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)
Descargo de responsabilidad: el código muestra la idea general, puede hacerlo un poco más limpio (por ejemplo, escribir un decorador para hacer eso).
No puede hacer esto con simple xmlrpc ya que no tiene noción de argumentos de palabras clave. Sin embargo, puede superponerlo como un protocolo sobre xmlrpc que siempre pasará una lista como primer argumento, y un diccionario como segundo, y luego proporcione el código de soporte adecuado para que se vuelva transparente para su uso, ejemplo a continuación:
Servidor
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()
Cliente
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})
Usando el consejo anterior, creé un código de trabajo.
Envoltura del método del servidor:
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
Configuración del cliente (hacer una vez):
_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
Probé esto y es compatible con el método con argumentos fijos, posicionales y de palabras clave.
XML-RPC realmente no tiene un concepto de ''argumentos de palabra clave'', por lo que xmlrpclib no intenta darles soporte. Debería elegir una convención, luego modificar xmlrpclib._Method para aceptar argumentos de palabra clave y pasarlos usando esa convención.
Por ejemplo, solía trabajar con un servidor XML-RPC que pasaba argumentos de palabra clave como dos argumentos, ''-KEYWORD'' seguidos por el argumento real, en una lista plana. Ya no tengo acceso al código que escribí para acceder a ese servidor XML-RPC de Python, pero era bastante simple, en la línea 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
Utiliza monkeypatching porque es, de lejos, el método más fácil para hacer esto, debido a algunos usos torpes de los módulos globales y los atributos manidos por los nombres (__request, por ejemplo) en la clase ServerProxy.