15

pythonsSimpleXMLRPCServerを使用してリモートサービスとして公開したいクラスがあります。サーバーの起動は次のようになります。

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に転送され、xmlrpclib.Serverはそれをリモートサーバーに転送します。問題は、名前付きvarargsを受け取るサービスのメソッドです。

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

@useDbデコレータは関数をラップし、呼び出しの前にdbを作成して開き、呼び出しが完了した後、結果を返す前に閉じます。

このメソッドを呼び出すと、「call()が予期しないキーワード引数'name'」というエラーが発生します。それで、変数の名前付き引数をリモートで取るメソッドを呼び出すことは可能ですか?または、必要なメソッドバリエーションごとにオーバーライドを作成する必要がありますか。


回答ありがとうございます。コードを少し変更したので、質問はもう問題ではありません。ただし、実際に位置引数を実装してリモート呼び出しをサポートする必要がある場合は、将来の参照用にこれを知っています。トーマスとプラプタクスのアプローチの組み合わせが良いと思います。xmlrpclientを介してクライアントでkwargsを位置引数に変換し、サーバー側のメソッドにラッパーを設定して位置引数を解凍します。

4

5 に答える 5

15

キーワード引数の概念がないため、プレーンなxmlrpcではこれを行うことはできません。ただし、これをxmlrpcの上にプロトコルとして重ね合わせて、常に最初の引数としてリストを渡し、2番目の引数として辞書を渡すことができます。次に、適切なサポートコードを提供して、これが使用法に対して透過的になるようにします。以下の例を参照してください。

サーバ

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})
于 2008-09-23T10:47:37.040 に答える
4

XML-RPCには実際には「キーワード引数」の概念がないため、xmlrpclibはそれらをサポートしようとしません。規則を選択してから、xmlrpclib._Methodを変更してキーワード引数を受け入れ、その規則を使用してそれらを渡す必要があります。

たとえば、フラットリストでキーワード引数を2つの引数として渡した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

モンキーパッチを使用するのは、これを行うのがはるかに簡単な方法であるためです。これは、ServerProxyクラスでモジュールグローバルと名前マングル属性(たとえば、__ request)が不格好に使用されているためです。

于 2008-09-23T09:01:47.983 に答える
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)

免責事項:コードは一般的な考え方を示しています。少しすっきりさせることができます(たとえば、それを行うためのデコレータを作成します)。

于 2008-09-23T08:56:03.283 に答える
1

上記のアドバイスを使用して、いくつかの実用的なコードを作成しました。

サーバー メソッド ラッパー:

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

クライアントのセットアップ (1 回実行):

_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

これをテストしたところ、固定、位置、およびキーワード引数を使用したメソッドがサポートされています。

于 2008-09-23T10:26:43.753 に答える
0

Thomas Wouters が言ったように、XML-RPC にはキーワード引数がありません。プロトコルに関する限り、引数の順序のみが重要であり、XML ではどのような名前でも呼び出すことができます: arg0、arg1、arg2 はまったく問題なく、同じ引数のチーズ、キャンディー、ベーコンも同様です。

おそらく、プロトコルの使用を再考する必要がありますか? document/literal SOAP のようなものを使用することは、他の回答で示されているような回避策よりもはるかに優れています。もちろん、これは実現不可能かもしれません。

于 2008-09-23T09:06:05.257 に答える