簡體   English   中英

在Python中將** kwargs與SimpleXMLRPCServer一起使用

[英]Using **kwargs with SimpleXMLRPCServer in python

我有一類希望使用python 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裝飾器包裝函數,在調用之前創建數據庫並打開它,然后在調用完成后關閉它,然后返回結果。

當我調用此方法時,出現錯誤“ call ()得到了意外的關鍵字參數'name'”。 因此,是否可以遠程調用采用名為實參的變量的方法? 還是我必須為我需要的每個方法變體創建一個替代。


感謝您的答復。 我更改了一些代碼,因此問題不再是問題。 但是,如果我確實確實需要實現位置參數並支持遠程調用,那么現在我知道這一點,以供將來參考。 我認為將Thomas和praptaks方法結合起來會很好。 通過xmlrpclient在客戶端上將kwargs轉換為位置args,並在服務器端對方法進行包裝以解壓縮位置參數。

您不能使用純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})

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)進行了一些笨拙的使用。

據我所知,底層協議不支持命名的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)

免責聲明:代碼顯示了總體思路,您可以使其更加簡潔(例如,編寫裝飾器來實現)。

正如Thomas Wouters所說,XML-RPC沒有關鍵字參數。 就協議而言,僅參數的順序很重要,並且可以在XML中將其稱為任何東西:arg0,arg1,arg2非常好,相同參數的奶酪,糖果和培根也是如此。

也許您應該簡單地重新考慮對協議的使用? 使用諸如文檔/文字SOAP之類的東西比諸如此處其他答案中提出的變通辦法要好得多。 當然,這可能不可行。

根據以上建議,我創建了一些工作代碼。

服務器方法包裝器:

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

我對此進行了測試,它支持帶有固定,位置和關鍵字參數的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM