Alex
Alex

Reputation: 44385

How to wrap a xmlrpc function call?

I have a simple xmlrpc server defined as (server.py):

from SimpleXMLRPCServer import SimpleXMLRPCServer

def foo():
    return "foo"

server = SimpleXMLRPCServer(("localhost", 1025))
server.register_introspection_functions()
server.register_function(foo, 'test.foo')
server.serve_forever()

and the client (client.py) implemented as follows:

import xmlrpclib

class XMLRPC(object):

    def __init__(self):
        self.xmlrpc = xmlrpclib.ServerProxy("http://localhost:1025/RPC2")

    def __getattr__(self, name):
        attr = getattr(self.xmlrpc, name)
        def wrapper(*args, **kwargs):
            result = attr(*args, **kwargs)
            return result
        return wrapper

xmlrpc = XMLRPC()
print xmlrpc.test.foo()

I want to wrap and execute each call that is being made to the xmlrpc server within the class XMLRPC. But the above working example gives an error

Traceback (most recent call last):
  File "client.py", line 20, in <module>
    xmlrpc.test.foo()
AttributeError: 'function' object has no attribute 'foo'

How to make this code work?

Additional information and constraints:

Upvotes: 1

Views: 1863

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123400

You need to return a callable object instead.

The XML-RPC proxy returns an instance of an object that is callable, but can also be traversed over. So for xmlrpc.test.foo(), you are wrapping xmlrpc.test in a function; that function object does not have a foo attribute because functions generally don't have such attributes.

Return a proxy object instead; the __call__ hook makes it a callable object, just like a function would be:

class WrapperProxy(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        attr = getattr(self.wrapped, name)
       return type(self)(attr)

    def __call__(self, *args, **kw):
        return self.wrapped(*args, **kw)

class XMLRPC(object):
    def __init__(self):
        self.xmlrpc = xmlrpclib.ServerProxy("http://localhost:1025/RPC2")

    def __getattr__(self, name):
        attr = getattr(self.xmlrpc, name)
        return WrapperProxy(attr)

or, merged into one object:

class XMLRPCWrapperProxy(object):
    def __init__(self, wrapped=None):
        if wrapped is None: 
            wrapped = xmlrpclib.ServerProxy("http://localhost:1025/RPC2")
        self.wrapped = wrapped

    def __getattr__(self, name):
        attr = getattr(self.wrapped, name)
       return type(self)(attr)

    def __call__(self, *args, **kw):
        return self.wrapped(*args, **kw)

Upvotes: 4

Related Questions