Question

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:

  • I already tried to wrap wrapper with functools.wraps(attr) without succcess. As the string attr has no attribute __name__ I get a different error
  • I canot change anything defined in server.py.
  • The above example is fully working.
  • Replacing return wrapper by return attr is not a solution - I need to execute the actual xmlrpc call within wrapper.
  • I need a simple solution without a 3rd party library, standard python libs are ok.
Was it helpful?

Solution

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)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top