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:
wrapper
with functools.wraps(attr)
without succcess. As the string attr
has no attribute __name__
I get a different errorserver.py
.return wrapper
by return attr
is not a solution - I need to execute the actual xmlrpc
call within wrapper
.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)