包装函数调用xmlrpc服务器

时间:2012-12-08 12:53:06

标签: python attributes xml-rpc

我有一个运行xmlrpc的服务器,如下所示

from SimpleXMLRPCServer import SimpleXMLRPCServer

def add(x,y):
    return x+y

server = SimpleXMLRPCServer(("localhost", 8000))
server.register_function(add, 'add.numbers')
server.serve_forever()

在以下代码中使用:

import xmlrpclib

class DeviceProxy(object):
    def __init__(self, uri):
        self.rpc = xmlrpclib.ServerProxy(uri)
    def __getattr__(self, attr):
        return getattr(self.rpc, attr)

original = DeviceProxy.__getattr__

def mygetattr(device, attr):
    def wrapper(*args, **kw):
        print('called with %r and %r' % (args, kw))
        return original(device, attr)(*args, **kw)
    return wrapper

DeviceProxy.__getattr__ = mygetattr

dev = DeviceProxy("http://localhost:8000/RPC2")
print dev.add.numbers(4,6)

正如您所看到的,Proxy类包含xmlrpc代理的原因超出了此问题的范围,通过__getattr__方法转发任意调用。出于此问题范围之外的其他原因,我需要通过不同的方法将此__getattr__方法换行/替换为例如打印出所调用函数的名称,参数等(参见相关问题here)。

但是这种方法不起作用,它会出现以下错误:

AttributeError: 'function' object has no attribute 'numbers'

当我

时,该示例按预期工作
  • 不要用其他内容替换DeviceProxy.__getattr__
  • 用函数

    替换DeviceProxy.__getattr__

    def dummy(instance,attr):     return original(device,attr)

  • 用零点名称替换xmlrpc函数的名称(例如sum而不是sum.numbers

您可以验证以下通过xmlrpc代理直接调用将按预期工作:

dev = xmlrpclib.ServerProxy("http://localhost:8000/RPC2")
print dev.add.numbers(4,6)

我的问题:如何解决我的问题,即如何能够正确地包装/覆盖DeviceProxy.__getattr__以便能够看到所调用的函数,所有参数等,而无需在xmlrpc中进行更改服务器或DeviceProxy类?

1 个答案:

答案 0 :(得分:0)

我可以在这里看到两个问题:

  1. DeviceProxy的所有属性是否都起作用?如果他们不是,那么当有人想要一个对象时,你有时会返回一个函数
  2. 当你包装这个函数时,你不是要复制成员 - 使用functools.wraps来实现它。
  3. 这应该有用

    from functools import wraps
    
    @wraps(original)  # probably not needed, but sensible
    def mygetattr(device, key):
        attr = original(device, key)
        if callable(attr):
            @wraps(attr)  # copy across __name__, __dict__ etc
            def wrapper(*args, **kw):
                print('called with %r and %r' % (args, kw))
                return attr(*args, **kw)
            return wrapper
        else:  # handle (or rather, don't) non-callable attributes
            return attr