Подтвердить что ты не робот

Использование ** kwargs с SimpleXMLRPCServer в python

У меня есть класс, который я хочу показать как удаленный сервис с помощью 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 перед вызовом и открывая ее, затем закрывая ее после завершения вызова перед возвратом результата.

Когда я вызываю этот метод, я получаю сообщение об ошибке " вызов() получил неожиданное ключевое слово" имя ". Итак, можно ли вызывать методы, использующие переменные именованные аргументы удаленно? Или мне нужно создать переопределение для каждого варианта метода, который мне нужен.


Спасибо за ответы. Я немного изменил свой код, поэтому вопрос больше не является проблемой. Однако теперь я знаю это для будущей справки, если мне действительно нужно реализовать позиционные аргументы и поддерживать удаленный вызов. Я думаю, что сочетание подходов Томаса и Праптакса было бы хорошим. Превращение kwargs в позиционные args на клиенте через xmlrpclient и наличие оболочки на методах serverside для распаковки позиционных аргументов.

4b9b3361

Ответ 1

Вы не можете сделать это с помощью простого 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()

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

Ответ 2

XML-RPC на самом деле не имеет понятия "аргументы ключевого слова", поэтому xmlrpclib не пытается их поддерживать. Вам нужно будет выбрать соглашение, а затем изменить xmlrpclib._Method, чтобы принять аргументы ключевого слова и передать их, используя это соглашение.

Например, я работал с сервером XML-RPC, который передавал аргументы ключевого слова в виде двух аргументов: "-KEYWORD", за которым следует фактический аргумент, в плоском списке. У меня больше нет доступа к коду, который я написал для доступа к этому XML-RPC-серверу из Python, но он был довольно прост в соответствии с:

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, потому что это самый простой способ сделать это из-за каких-то неуклюжих применений глобалов модулей и атрибутов с именами (например, __request) в классе ServerProxy.

Ответ 3

Насколько я знаю, базовый протокол не поддерживает именованные varargs (или любые именованные аргументы, если на то пошло). Обходной путь для этого заключается в создании обертки, которая будет принимать ** 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)

Отказ от ответственности: код показывает общую идею, вы можете сделать это немного чище (например, написание декоратора для этого).

Ответ 4

Как сказал Томас Воутерс, XML-RPC не имеет аргументов ключевого слова. Только аргумент аргументов имеет значение в отношении протокола, и их можно назвать чем угодно в XML: arg0, arg1, arg2 отлично, как и сыр, конфеты и бекон для тех же самых аргументов.

Возможно, вам стоит просто пересмотреть свое использование протокола? Использование чего-то типа document/literal SOAP было бы намного лучше, чем обходной путь, такой как представленные в других ответах здесь. Конечно, это может оказаться невыполнимым.

Ответ 5

Используя приведенный выше совет, я создал некоторый рабочий код.

Оболочка сервера:

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

Я тестировал это, и он поддерживает метод с фиксированными, позиционными и ключевыми аргументами.