I need to generate a class that would mimic another class's method set and behave like the latter via a proxy. e.g. If Base
is the class to mimic and Deleguate
is the class which needs to act as Base
then:
b = Base(args)
b.any_function()
is strictly equivalent to
d = Deleguate(b)
d.any_function()
If Deleguate
uses a function that already exists in Base
, it won't be overwritten. It's the kind of behavior you expect with inheritance and method overriding. Inheritance is not an option in the context of the project I'm working on (among other constraint, I don't have access to the factory code). And that's what makes things complicated.
I therefore decided to code a "proxy" decorator :
import inspect
def proxy(bridge, target):
def proxyfy(cls):
for _, func in inspect.getmembers(target, predicate=inspect.ismethod):
fname = func.__name__
if fname in cls.__dict__:
print 'ignoring %s.%s' % (cls, fname)
continue
print 'adding %s.%s' % (cls, fname)
def proxy_func(self, *args, **kwargs):
print 'calling %s.%s.%s' % (cls, bridge, fname)
bridge_member = getattr(self, bridge)
return getattr(bridge_member, fname)(*args, **kwargs)
setattr(cls, fname, proxy_func)
return cls
return proxyfy
class Base(object):
def __init__(self, i):
self._i = i
def __bar(self):
print 0
def foo(self):
print self._i
def foo2(self):
print 2 * self._i
@proxy('_proxy', Base)
class Deleguate(object):
def __init__(self, base):
self._proxy = base
def foo2(self):
print 4 * self._proxy._i
d = Deleguate(Base(1))
d.__bar() # d._proxy.__bar()
d.foo() # d._proxy.foo()
d.foo2() # d.foo2()
I get the following output :
adding <class '__main__.Deleguate'>.__bar
ignoring <class '__main__.Deleguate'>.__init__
adding <class '__main__.Deleguate'>.foo
ignoring <class '__main__.Deleguate'>.foo2
calling <class '__main__.Deleguate'>._proxy.foo2
2
calling <class '__main__.Deleguate'>._proxy.foo2
2
4
I thought that setattr(cls, fname, proxy_func)
would assign a new closure, but the arguments are overwritten at each loop step and only the arguments of the last function foo2
are kept. Therefore calling any "generated" function of Deleguate
uses foo2
arguments...
Why are the closure arguments being overwritten ? Is there a way to generate that kind of proxy code ? The expected output is :
adding <class '__main__.Deleguate'>.__bar
ignoring <class '__main__.Deleguate'>.__init__
adding <class '__main__.Deleguate'>.foo
ignoring <class '__main__.Deleguate'>.foo2
calling <class '__main__.Deleguate'>._proxy.__bar
0
calling <class '__main__.Deleguate'>._proxy.foo
1
4