classification
Title: descriptor protocol bug
Type: behavior Stage:
Components: Interpreter Core Versions: Python 3.3, Python 3.2, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: BreamoreBoy, Micah.Friesen, benjamin.peterson, daniel.urban, eric.araujo, gangesmaster, pitrou, rhettinger, thomas.lee
Priority: high Keywords:

Created on 2007-12-13 20:13 by gangesmaster, last changed 2012-04-18 22:07 by eric.araujo.

Files
File name Uploaded Description Edit
demo.txt gangesmaster, 2007-12-13 20:13
Messages (8)
msg58581 - (view) Author: ganges master (gangesmaster) Date: 2007-12-13 20:13
it seems the code of PyObject_GenericGetAttr, which invokes the
descriptor protocol, silences any AttributeErrors raised by the
descriptor, for classes that also define __getattr__. it should
propagate up rather than being silently ignored.

the attached example is quite artificial, but it's a simplification of
real world code i had hard time debugging. turned out i misspelled an
attribute name inside the property getter function, which raised an
AttributeError as expected -- but the exception i got was quite
misleading, saying the instance has no attribute named so.

this bug only happens when the class defines a custom __getattr__. see
attached demo file for details.
msg61312 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2008-01-20 17:29
I can confirm that with SVN trunk, and it's actually even worse because
it can return unexpected results without raising an exception at all:

>>> class Foo(object):
...   def __getattr__(self, name): return 42
...   @property
...   def bacon(self): return int.lalala
... 
>>> f = Foo()
>>> f.bacon
42
>>> Foo.bacon.__get__(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in bacon
AttributeError: type object 'int' has no attribute 'lalala'
msg61321 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2008-01-20 18:09
PyObject_GenericGetAttr is invoked from slot_tp_getattr_hook in
typeobject.c via tp_getattro. The problem is that, when tp_getattro
returns with an AttributeError, there is no way for slot_tp_getattr_hook
to know whether the error was raised by PyObject_GenericGetAttr itself
or by subsequent invocation of user code. Perhaps by adding an attribute
to the raised AttributeError?
msg76825 - (view) Author: ganges master (gangesmaster) Date: 2008-12-03 12:20
here's a short example of the bug

>>> class Foo(object):
...   def __getattr__(self, name): 
...     return 42
...   @property
...   def bacon(self): 
...     return int.lalala
...   @property
...   def eggs(self): 
...     return 17
... 
>>> f = Foo()
>>> f.bacon   # raises an AttributeError, and silently ignores it
42
>>> f.eggs
17
>>> 

are there any news in this front?
msg77424 - (view) Author: Thomas Lee (thomas.lee) (Python committer) Date: 2008-12-09 15:20
Related reading from a few years back:

http://coding.derkeiler.com/Archive/Python/comp.lang.python/2005-05/msg03829.html
msg116804 - (view) Author: Mark Lawrence (BreamoreBoy) Date: 2010-09-18 15:53
This is still an issue with the latest trunk.
msg116805 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2010-09-18 15:57
I consider this to be a feature. Properties can raise AttributeError to defer to __getattr__.
msg158556 - (view) Author: Micah Friesen (Micah.Friesen) Date: 2012-04-17 16:16
I ran into this recently, as well, and have lost probably a day's worth of time debugging it. I submit that this is not a feature - I can't imagine a real-world scenario where you actually want to write debuggable code where a descriptor defers to __getattr__ (except perhaps for exception handling, in which case some re-factoring is in order), particularly because descriptors are effectively mix-ins and can be used on multiple classes.

I worked around this by writing an ancestor descriptor that catches AttributeErrors and re-raises them as a user-defined exception.
History
Date User Action Args
2012-04-18 22:07:08eric.araujosetnosy: + eric.araujo

versions: + Python 3.2, Python 3.3, - Python 2.6
2012-04-17 23:38:11pitrousetnosy: + rhettinger
2012-04-17 16:16:30Micah.Friesensetnosy: + Micah.Friesen
messages: + msg158556
2010-09-18 15:57:02benjamin.petersonsetnosy: + benjamin.peterson
messages: + msg116805
2010-09-18 15:53:48BreamoreBoysetnosy: + BreamoreBoy
messages: + msg116804
2010-08-16 17:28:35daniel.urbansetnosy: + daniel.urban
2010-05-11 20:41:41terry.reedysetversions: + Python 2.7, - Python 2.5
2008-12-09 15:20:54thomas.leesetnosy: + thomas.lee
messages: + msg77424
2008-12-03 12:20:40gangesmastersetmessages: + msg76825
2008-01-20 20:01:53christian.heimessetpriority: high
2008-01-20 18:09:29pitrousetmessages: + msg61321
2008-01-20 17:29:07pitrousetnosy: + pitrou
messages: + msg61312
severity: normal -> major
2007-12-13 20:13:37gangesmastercreate