简体   繁体   English

在Python中将** kwargs与SimpleXMLRPCServer一起使用

[英]Using **kwargs with SimpleXMLRPCServer in python

I have a class that I wish to expose as a remote service using pythons SimpleXMLRPCServer. 我有一类希望使用python SimpleXMLRPCServer公开为远程服务的类。 The server startup looks like this: 服务器启动看起来像这样:

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

service = Service()

server.register_instance(service)
server.serve_forever()

I then have a ServiceRemote class that looks like this: 然后,我有一个如下所示的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)

So all calls on the ServiceRemote object will be forwarded to xmlrpclib.Server, which then forwards it to the remote server. 因此,对ServiceRemote对象的所有调用都将转发到xmlrpclib.Server,然后将其转发到远程服务器。 The problem is a method in the service that takes named varargs: 问题是服务中采用名为varargs的方法:

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

The @useDb decorator wraps the function, creating the db before the call and opening it, then closing it after the call is done before returning the result. @useDb装饰器包装函数,在调用之前创建数据库并打开它,然后在调用完成后关闭它,然后返回结果。

When I call this method, I get the error " call () got an unexpected keyword argument 'name'". 当我调用此方法时,出现错误“ call ()得到了意外的关键字参数'name'”。 So, is it possible to call methods taking variable named arguments remotely? 因此,是否可以远程调用采用名为实参的变量的方法? Or will I have to create an override for each method variation I need. 还是我必须为我需要的每个方法变体创建一个替代。


Thanks for the responses. 感谢您的答复。 I changed my code around a bit so the question is no longer an issue. 我更改了一些代码,因此问题不再是问题。 However now I know this for future reference if I indeed do need to implement positional arguments and support remote invocation. 但是,如果我确实确实需要实现位置参数并支持远程调用,那么现在我知道这一点,以供将来参考。 I think a combination of Thomas and praptaks approaches would be good. 我认为将Thomas和praptaks方法结合起来会很好。 Turning kwargs into positional args on the client through xmlrpclient, and having a wrapper on methods serverside to unpack positional arguments. 通过xmlrpclient在客户端上将kwargs转换为位置args,并在服务器端对方法进行包装以解压缩位置参数。

You can't do this with plain xmlrpc since it has no notion of keyword arguments. 您不能使用纯xmlrpc做到这一点,因为它没有关键字参数的概念。 However, you can superimpose this as a protocol on top of xmlrpc that would always pass a list as first argument, and a dictionary as a second, and then provide the proper support code so this becomes transparent for your usage, example below: 但是,您可以将其作为xmlrpc之上的协议进行叠加,该协议始终将列表作为第一个参数传递,将字典作为第二个参数传递,然后提供适当的支持代码,因此这对于您的用法而言是透明的,例如以下示例:

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

XML-RPC doesn't really have a concept of 'keyword arguments', so xmlrpclib doesn't try to support them. XML-RPC实际上没有“关键字参数”的概念,因此xmlrpclib不会尝试支持它们。 You would need to pick a convention, then modify xmlrpclib._Method to accept keyword arguments and pass them along using that convention. 您将需要选择一个约定,然后修改xmlrpclib._Method以接受关键字参数并使用该约定传递它们。

For instance, I used to work with an XML-RPC server that passed keyword arguments as two arguments, '-KEYWORD' followed by the actual argument, in a flat list. 例如,我曾经使用过XML-RPC服务器,该服务器在平面列表中将关键字参数作为两个参数传递,“-KEYWORD”后跟实际参数。 I no longer have access to the code I wrote to access that XML-RPC server from Python, but it was fairly simple, along the lines of: 我不再可以访问编写的代码以从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

It uses monkeypatching because that's by far the easiest method to do this, because of some clunky uses of module globals and name-mangled attributes (__request, for instance) in the ServerProxy class. 它使用Monkeypatching,因为到目前为止,这是最简单的方法,因为在ServerProxy类中对模块全局变量和名称混杂属性(例如__request)进行了一些笨拙的使用。

As far as I know, the underlying protocol doesn't support named varargs (or any named args for that matter). 据我所知,底层协议不支持命名的varargs(或与此有关的任何命名的args)。 The workaround for this is to create a wrapper that will take the **kwargs and pass it as an ordinary dictionary to the method you want to call. 解决方法是创建一个包装器,该包装器将使用** kwargs并将其作为普通字典传递给要调用的方法。 Something like this 像这样

Server side: 服务器端:

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

On the client side: 在客户端:

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: the code shows the general idea, you can do it a bit cleaner (for example writing a decorator to do that). 免责声明:代码显示了总体思路,您可以使其更加简洁(例如,编写装饰器来实现)。

As Thomas Wouters said, XML-RPC does not have keyword arguments. 正如Thomas Wouters所说,XML-RPC没有关键字参数。 Only the order of arguments matters as far as the protocol is concerned and they can be called anything in XML: arg0, arg1, arg2 is perfectly fine, as is cheese, candy and bacon for the same arguments. 就协议而言,仅参数的顺序很重要,并且可以在XML中将其称为任何东西:arg0,arg1,arg2非常好,相同参数的奶酪,糖果和培根也是如此。

Perhaps you should simply rethink your use of the protocol? 也许您应该简单地重新考虑对协议的使用? Using something like document/literal SOAP would be much better than a workaround such as the ones presented in other answers here. 使用诸如文档/文字SOAP之类的东西比诸如此处其他答案中提出的变通办法要好得多。 Of course, this may not be feasible. 当然,这可能不可行。

Using the above advice, I created some working code. 根据以上建议,我创建了一些工作代码。

Server method wrapper: 服务器方法包装器:

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

Client setup (do once): 客户端设置(执行一次):

_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

I tested this, and it supports method with fixed, positional and keyword arguments. 我对此进行了测试,它支持带有固定,位置和关键字参数的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM