Accessing `super()` inside python class property - python

I'm learning about python's inheritance, and have come across a behaviour I don't quite understand. Here is a minimal working example:
class Test():
def meth1(self):
print('accessing meth1')
return super().a #calling random nonexisting attribute; error (as expected)
#property
def prop1(self):
print('accessing prop1')
return super().a #calling random nonexisting attribute; no error?
def __getattr__(self, name):
print('getattr ' + name)
test = Test()
Calling .meth1() fails as expected...
In [1]: test.meth1()
accessing meth1
Traceback (most recent call last):
File "<ipython-input-160-4a0675c95211>", line 1, in <module>
test.meth1()
File "<ipython-input-159-1401fb9a0e13>", line 5, in meth1
return super().a #calling random nonexisting attribute; error (as expected)
AttributeError: 'super' object has no attribute 'a'
...as super() is object which does indeed not have this attribute.
But .prop1 does not...
In [2]: test.prop1
accessing prop1
getattr prop1
...which I don't understand. It seems the property is called twice, once 'normally' and once via __getattr__.
Some observations:
I assume it's got something to do with the property decorator.
The attribute .a seems to never be accessed.
If I replace the return super().a line in prop1 with something like return 5, the __getattr__ method is never called.
If I actually make Test inherit from a class having an attribute a, its value is returned from test.meth1(), but not from test.prop1.
Could someone explain what's going on here? I've not been able to find any useful information addressing the combination of attribute decorators and super().
Many thanks,

TLDR: meth1 raises AttributeError after lookup, when __getattr__ is not involved. prop1 raises AttributeError during lookup, triggering a fallback to __getattr__ which succeeds to return None.
>>> test.prop1 # AttributeError happens here during lookup
accessing prop1
getattr prop1
>>> meth = test.meth1 # AttributeError happens *not* here during lookup
>>> meth() # AttributeError happens here *after* lookup
...
AttributeError: 'super' object has no attribute 'a'
The __getattr__ method is only called when an "attribute is not found" – in other words that AttributeError is raised on access. The same behaviour occurs when the property raises the error directly:
class Test():
#property
def prop1(self):
print('accessing prop1')
raise AttributeError # replaces `super().a`
def __getattr__(self, name):
print('getattr ' + name)
test = Test()
test.prop1 # < explicitly raises AttributeError
# accessing prop1
# getattr prop1
test.prop2 # < implicitly raises AttributeError
# getattr prop2
The AttributeError does not reveal whether it comes from a missing prop1 attribute or some nested internal attribute (say, super().a). Thus, both trigger the fallback to __getattr__.
This is intended behaviour of __getattr__.
object.__getattr__(self, name)
Called when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or __get__() of a name property raises AttributeError).
It allows properties to fallback to the regular lookup mechanism when they cannot produce a value.

Related

Decorate class methods dynamically raises error in python

I am following this answer in order to decorate all methods of a class. This is my version of the code:
def decorate_object_methods(object_to_decorate, decorator):
for name in dir(object_to_decorate):
attr = getattr(object_to_decorate, name)
if callable(attr):
wrapped = decorator(attr)
setattr(object_to_decorate, name, wrapped)
The problem is that I am getting this exception in the last sentence of the method:
TypeError: __class__ must be set to a class, not 'function' object
I can't find what I'm doing wrong

How to make an Python subclass uncallable

How do you "disable" the __call__ method on a subclass so the following would be true:
class Parent(object):
def __call__(self):
return
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
object.__setattr__(self, '__call__', None)
>>> c = Child()
>>> callable(c)
False
This and other ways of trying to set __call__ to some non-callable value still result in the child appearing as callable.
You can't. As jonrsharpe points out, there's no way to make Child appear to not have the attribute, and that's what callable(Child()) relies on to produce its answer. Even making it a descriptor that raises AttributeError won't work, per this bug report: https://bugs.python.org/issue23990 . A python 2 example:
>>> class Parent(object):
... def __call__(self): pass
...
>>> class Child(Parent):
... __call__ = property()
...
>>> c = Child()
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: unreadable attribute
>>> c.__call__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: unreadable attribute
>>> callable(c)
True
This is because callable(...) doesn't act out the descriptor protocol. Actually calling the object, or accessing a __call__ attribute, involves retrieving the method even if it's behind a property, through the normal descriptor protocol. But callable(...) doesn't bother going that far, if it finds anything at all it is satisfied, and every subclass of Parent will have something for __call__ -- either an attribute in a subclass, or the definition from Parent.
So while you can make actually calling the instance fail with any exception you want, you can't ever make callable(some_instance_of_parent) return False.
It's a bad idea to change the public interface of the class so radically from the parent to the base.
As pointed out elsewhere, you cant uninherit __call__. If you really need to mix in callable and non callable classes you should use another test (adding a class attribute) or simply making it safe to call the variants with no functionality.
To do the latter, You could override the __call__ to raise NotImplemented (or better, a custom exception of your own) if for some reason you wanted to mix a non-callable class in with the callable variants:
class Parent(object):
def __call__(self):
print "called"
class Child (Parent):
def __call__(self):
raise NotACallableInstanceException()
for child_or_parent in list_of_children_and_parents():
try:
child_or_parent()
except NotACallableInstanceException:
pass
Or, just override call with pass:
class Parent(object):
def __call__(self):
print "called"
class Child (Parent):
def __call__(self):
pass
Which will still be callable but just be a nullop.

Property and __getattr__ compatibility issue with AttributeError

I just encountered an unexpected behavior. This is a simple class with a __getattr__ method and a property attribute with a typo inside:
class A(object):
def __getattr__(self, attr):
if not attr.startswith("ignore_"):
raise AttributeError(attr)
#property
def prop(self):
return self.some_typo
a = A() # Instantiating
a.ignore_this # This is ignored
a.prop # This raises an Attribute Error
This is the expected outcome (the one I get if __getattr__ is commented):
AttributeError: 'A' object has no attribute 'some_typo'
And this is what I get:
AttributeError: prop
I know this has to do with__getattr__ catching the AttributeError but is there a nice and clean workaround for this issue? Because I can assure you, this is a debug nightmare...
You can just raise a better exception message:
class A(object):
def __getattr__(self, attr):
if not attr.startswith("ignore_"):
raise AttributeError("%r object has not attribute %r" % (self.__class__.__name__, attr))
#property
def prop(self):
return self.some_typo
a=A()
a.ignore_this
a.prop
EDIT: calling __getattribute__ from object base class solves the problem
class A(object):
def __getattr__(self, attr):
if not attr.startswith("ignore_"):
return self.__getattribute__(attr)
#property
def prop(self):
return self.some_typo
As mentioned by #asmeurer, the solution by #mguijarr calls prop twice. When prop first runs, it raises an AttributeError which triggers __getattr__. Then self.__getattribute__(attr) triggers prop again, finally resulting in the desired exception.
BETTER ANSWER:
Here we are better off replacing __getattribute__ instead of __getattr__. It gives us more control since __getattribute__ is invoked on all attribute access. In contrast, __getattr__ is only called when there has already been an AttributeError, and it doesn't give us access to that original error.
class A(object):
def __getattribute__(self, attr):
try:
return super().__getattribute__(attr)
except AttributeError as e:
if not attr.startswith("ignore_"):
raise e
#property
def prop(self):
print("hi")
return self.some_typo
To explain, since A subclasses object in this case, super().__getattribute__(attr) is equivalent to object.__getattribute__(self, attr). That reads a's underlying object attribute, avoiding the infinite recursion had we instead used self.__getattribute__(attr).
In case of AttributeError, we have full control to either fail or reraise, and reraising gives a sensible error message.

Python object proxying: how to access proxy

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.
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')().

Python: Why is __getattr__ catching AttributeErrors?

I'm struggling with __getattr__. I have a complex recursive codebase, where it is important to let exceptions propagate.
class A(object):
#property
def a(self):
raise AttributeError('lala')
def __getattr__(self, name):
print('attr: ', name)
return 1
print(A().a)
Results in:
('attr: ', 'a')
1
Why this behaviour? Why is no exception thrown? This behaviour is not documented (__getattr__ documentation). getattr() could just use A.__dict__. Any thoughts?
I just changed the code to
class A(object):
#property
def a(self):
print "trying property..."
raise AttributeError('lala')
def __getattr__(self, name):
print('attr: ', name)
return 1
print(A().a)
and, as we see, indeed the property is tried first. But as it claims not to be there (by raising AttributeError), __getattr__() is called as "last resort".
It is not documented clearly, but can maybe be counted under "Called when an attribute lookup has not found the attribute in the usual places".
Using __getattr__ and properties in the same class is dangerous, because it can lead to errors that are very difficult to debug.
If the getter of a property throws AttributeError, then the AttributeError is silently caught, and __getattr__ is called. Usually, this causes __getattr__ to fail with an exception, but if you are extremely unlucky, it doesn't, and you won't even be able to easily trace the problem back to __getattr__.
EDIT: Example code for this problem can be found in this answer.
Unless your property getter is trivial, you can never be 100% sure it won't throw AttributeError. The exception may be thrown several levels deep.
Here is what you could do:
Avoid using properties and __getattr__ in the same class.
Add a try ... except block to all property getters that are not trivial
Keep property getters simple, so you know they won't throw AttributeError
Write your own version of the #property decorator, which catches AttributeError and re-throws it as RuntimeError.
See also http://blog.devork.be/2011/06/using-getattr-and-property_17.html
EDIT: In case anyone is considering solution 4 (which I don't recommend), it can be done like this:
def property_(f):
def getter(*args, **kwargs):
try:
return f(*args, **kwargs)
except AttributeError as e:
raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2]
return property(getter)
Then use #property_ instead of #property in classes that override __getattr__.
__getattribute__ documentation says:
If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError.
I read this (by inclusio unius est exclusio alterius) as saying that attribute access will call __getattr__ if object.__getattribute__ (which is "called unconditionally to implement attribute accesses") happens to raise AttributeError - whether directly or inside a descriptor __get__ (e.g. a property fget); note that __get__ should "return the (computed) attribute value or raise an AttributeError exception".
As an analogy, operator special methods can raise NotImplementedError whereupon the other operator methods (e.g. __radd__ for __add__) will be tried.
__getattr__ is called when an attribute access fails with an AttributeError. Maybe this is why you think it 'catches' the errors. However, it doesn't, it's Python's attribute access functionality that catches them, and then calls __getattr__.
But __getattr__ itself doesn't catch any errors. If you raise an AttributeError in __getattr__ you get infinite recursion.
regularly run into this problem because I implement __getattr__ a lot and have lots of #property methods. Here's a decorator I came up with to get a more useful error message:
def replace_attribute_error_with_runtime_error(f):
#functools.wraps(f)
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except AttributeError as e:
# logging.exception(e)
raise RuntimeError(
'{} failed with an AttributeError: {}'.format(f.__name__, e)
)
return wrapped
And use it like this:
class C(object):
def __getattr__(self, name):
...
#property
#replace_attribute_error_with_runtime_error
def complicated_property(self):
...
...
The error message of the underlying exception will include name of the class whose instance raised the underlying AttributeError.
You can also log it if you want to.
You're doomed anyways when you combine #property with __getattr__:
class Paradise:
pass
class Earth:
#property
def life(self):
print('Checking for paradise (just for fun)')
return Paradise.breasts
def __getattr__(self, item):
print("sorry! {} does not exist in Earth".format(item))
earth = Earth()
try:
print('Life in earth: ' + str(earth.life))
except AttributeError as e:
print('Exception found!: ' + str(e))
Gives the following output:
Checking for paradise (just for fun)
sorry! life does not exist in Earth
Life in earth: None
When your real problem was with calling Paradise.breasts.
__getattr__ is always called when an AtributeError is risen. The content of the exception is ignored.
The sad thing is that there's no solution to this problem given hasattr(earth, 'life') will return True (just because __getattr__ is defined), but will still be reached by the attribute 'life' as it didn't exist, whereas the real underlying problem is with Paradise.breasts.
My partial solution involves using a try-except in #property blocks which are known to hit upon AttributeError exceptions.

Categories