我有一个类,我希望使用pythons SimpleXMLRPCServer作为远程服务公开。服务器启动如下所示:
server = SimpleXMLRPCServer((serverSettings.LISTEN_IP,serverSettings.LISTEN_PORT))
service = Service()
server.register_instance(service)
server.serve_forever()
然后我有一个ServiceRemote类,如下所示:
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)
因此,对ServiceRemote对象的所有调用都将转发到xmlrpclib.Server,然后将其转发到远程服务器。问题是服务中采用名为varargs的方法:
@useDb
def select(self, db, fields, **kwargs):
pass
@useDb装饰器包装函数,在调用之前创建db并打开它,然后在调用完成之后关闭它,然后返回结果。
当我调用此方法时,我收到错误“调用()得到一个意外的关键字参数'name'”。那么,是否可以远程调用采用变量命名参数的方法?或者我是否必须为我需要的每个方法变体创建一个覆盖。
感谢您的回复。我改变了我的代码,所以问题不再是问题。但是现在我知道这个以供将来参考,如果我确实需要实现位置参数并支持远程调用。我认为Thomas和praptaks方法的结合会很好。通过xmlrpclient将kwargs转换为客户端上的位置args,并在方法服务器上使用包装器来解压位置参数。
答案 0 :(得分:13)
你不能用普通的xmlrpc做到这一点,因为它没有关键字参数的概念。但是,您可以将此作为协议叠加在xmlrpc之上,该协议始终将列表作为第一个参数传递,将字典作为第二个参数传递,然后提供适当的支持代码,以便对您的使用透明,例如:
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()
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})
答案 1 :(得分:3)
XML-RPC实际上没有“关键字参数”的概念,因此xmlrpclib不会尝试支持它们。您需要选择一个约定,然后修改xmlrpclib._Method以接受关键字参数并使用该约定传递它们。
例如,我曾经使用过一个XML-RPC服务器,该服务器在一个平面列表中将关键字参数作为两个参数传递,“-KEYWORD”后跟实际参数。我不再能够访问我为从Python访问XML-RPC服务器而编写的代码,但它非常简单,类似于:
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
它使用monkeypatching,因为到目前为止这是最简单的方法,因为在ServerProxy类中使用了模块全局变量和名称错位的属性(例如__request)。
答案 2 :(得分:1)
据我所知,底层协议不支持命名的varargs(或任何已命名的args)。解决方法是创建一个包装器,它将获取** kwargs并将其作为普通字典传递给您要调用的方法。像这样的东西
服务器端:
def select_wrapper(self, db, fields, kwargs):
"""accepts an ordinary dict which can pass through xmlrpc"""
return select(self,db,fields, **kwargs)
在客户端:
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)
免责声明:代码显示了一般的想法,你可以做得更清洁(例如写一个装饰器来做)。
答案 3 :(得分:0)
正如Thomas Wouters所说,XML-RPC没有关键字参数。只有协议的顺序才与协议有关,它们可以用XML调用:arg0,arg1,arg2完全没问题,奶酪,糖果和培根也是如此。
也许您应该重新考虑使用协议?使用像document / literal SOAP这样的东西会比解决方法好很多,例如其他答案中提供的解决方法。当然,这可能不太可行。
答案 4 :(得分:0)
使用上述建议,我创建了一些工作代码。
服务器方法包装器:
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
客户端设置(执行一次):
_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
我对此进行了测试,它支持使用固定,位置和关键字参数的方法。