3

I found this recipe to create a proxy class. I've used it to wrap a custom object and would like to overload certain properties and also attach new attributes to the proxy. However, when I call any method on the proxy (from within the proxy class), I end up being delegated to the wrappee which is not what I want.

Is there any way of accessing or storing a reference to the proxy?

Here's some code (untested) to demonstrate the problem.

class MyObject(object):
  @property
  def value(self):
    return 42

class MyObjectProxy(Proxy): # see the link above
  def __getattribute__(self, attr):
    # the problem is that `self` refers to the proxied 
    # object and thus this throws an AttributeError. How 
    # can I reference MyObjectProxy.another_value()?
    if attr == 'value':  return self.another_value() # return method or attribute, doesn't matter (same effect)
    return super(MyObjectProxy, self).__getattribute__(attr)

  def another_value(self):
    return 21

o = MyObject()
p = MyObjectProxy(o)
print o.value
print p.value

In a sense my problem is that the proxy works too good, hiding all its own methods/attributes and posing itself as the proxied object (which is what it should do)...

Update

Based on the comments below, I changed __getattribute__ to this:

def __getattribute__(self, attr):
    try:
        return object.__getattribute__(self, attr)
    except AttributeError:
        return super(MyObjectProxy, self).__getattribute__(attr)

This seems to do the trick for now, but it would be better to add this directly to the Proxy class.

orange
  • 7,755
  • 14
  • 75
  • 139

1 Answers1

1

The reason that your code goes wrong is the loop in __getattribute__. You want to override __getattribute__ so you can reach certain properties in the proxy class itself. But let's see.

When you call p.value the __getattribute__ is called. Then it comes here if attr == 'value': return self.another_value(). Here we need to call another_value so we enter __getattribute__ again.

This time we comes here return super(MyObjectProxy, self).__getattribute__(attr). We call the Proxy's __getattribute__, and it tries to fetch another_value in Myobject. So the exceptions occur.

You can see from the traceback that we finally goes to return super(MyObjectProxy, self).__getattribute__(attr) that should not go to.

Traceback (most recent call last):
  File "proxytest.py", line 22, in <module>
    print p.value
  File "proxytest.py", line 13, in __getattribute__
    if attr == 'value':  return self.another_value() # return method or attribute, doesn't matter (same effect)
  File "proxytest.py", line 14, in __getattribute__
    return super(MyObjectProxy, self).__getattribute__(attr)
  File "/home/hugh/m/tspace/proxy.py", line 10, in __getattribute__
    return getattr(object.__getattribute__(self, "_obj"), name)
AttributeError: 'MyObject' object has no attribute 'another_value'

edit:
Change the line of code if attr == 'value': return self.another_value() to if attr == 'value': return object.__getattribute__(self, 'another_value')().

zhangyangyu
  • 8,520
  • 2
  • 33
  • 43
  • Thanks for reproducing and confirming the above, @zhangyangyu. Any suggestions on how to get the desired behaviour? – orange Jul 11 '13 at 04:09
  • I thought I had tried that before, but I must have made a mistake. Your solutions works fine. I'll have a look at fixing the `proxy` class to look for its own attributes first before accessing the proxied one's. – orange Jul 11 '13 at 04:41
  • Fixing the `Proxy` class directly seems like a bigger problem than I thought. I tried to call `hasattr(self, attr)` in `__getattribute__`, but this seems to end in an infinite loop (is `hasattr` really calling `__getattribute`?) – orange Jul 12 '13 at 02:27
  • Yes. Read [here](http://stackoverflow.com/questions/12872695/which-special-methods-bypasses-getattribute-in-python).@orange – zhangyangyu Jul 12 '13 at 02:30
  • Nasty! I updated my question with your answer and what I found at the post you linked. – orange Jul 12 '13 at 03:12