Issue1095

classification
Title: AttributeError raised from __getattr__ method are swallowed
Type: behaviour Severity: normal
Components: Core Versions: 2.2.2, Jython 2.1, 2.5alpha1
Milestone:
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: leosoto Nosy List: akruis, leosoto, pjenvey
Priority: Keywords:

Created on 2008-08-04.09:51:09 by akruis, last changed 2008-08-21.03:24:35 by leosoto.

Files
File name Uploaded Description Edit Remove
testAttributeError.py akruis, 2008-08-04.10:12:52 A test script, that shows the difference between CPython and Jython
1095.diff akruis, 2008-08-19.11:58:36 patch for this issue. against r5155
Messages
msg3392 (view) Author: Anselm Kruis (akruis) Date: 2008-08-04.10:12:52
Sorry, I accidentally hit the Return-key while creating the ticket. 

Here is the description:

Jython catches AttributeErrors (and subclasses thereof) raised by
__getattr__(self, name) and replaces the exception by the default 
AttributeError. CPython does not show this behaviour.

Running the attached test script with the latest 2.5a release of Jython
gives:
> ~/src/jython2.5a1+/jython testAttributeError.py
Caught exception is different from raised exception
    Caught: class <type 'exceptions.AttributeError'> ''instance' object
has no attribute 'xyz''
    Raised: class <class '__main__.DerivedAttributeError'> 'xyz'

On CPython, the output of the attached test script is:
Caught exception is raised exception

This problem is very similar to #1041. "AttributeError raised from
descriptors __get__ method are swallowed"

Why is this difference a problem: 
I'm maintainer of an application, that uses jython as its embedded
scripting language. The API provided for users of this application shall
resemble CPython as much as possible and therefore hides some
implementation details using __getattr__ and similar methods. Errors on
attribute access usually raise an AttributeError and I'd prefer to stay
with that convention. However I need to pass some more information about
the real cause of the exception.
msg3396 (view) Author: Leonardo Soto (leosoto) Date: 2008-08-04.19:43:07
You are right, this is #1041 showing up in a different way. The good
news is that fixing that will fix this too.
msg3425 (view) Author: Leonardo Soto (leosoto) Date: 2008-08-11.23:28:17
Fixed in r5155
msg3436 (view) Author: Anselm Kruis (akruis) Date: 2008-08-19.09:54:24
I don't consider this bug fixed, because the attached script
"testAttributeError.py" still shows, that the exception raised by
__getattr__ gets swallowed.
msg3438 (view) Author: Anselm Kruis (akruis) Date: 2008-08-19.10:05:12
It might be noteworthy, that my test uses old-style classes. If I change
the test to use a new style class, it succeeds.
msg3439 (view) Author: Anselm Kruis (akruis) Date: 2008-08-19.11:58:36
The attached patch fixes this issue.

Some details: In my test case an old style class implements
__getattr__(self, name). An access of an otherwise undefined attribute
yields the following JAVA stack trace (just the interesting part):

        at org.python.core.PyFunction.__call__(PyFunction.java:312)
        at org.python.core.PyInstance.ifindfunction(PyInstance.java:238)
        at org.python.core.PyInstance.__findattr__(PyInstance.java:217)
        at org.python.core.PyInstance.__findattr_ex__(PyInstance.java:204)
        at org.python.core.PyObject.__getattr__(PyObject.java:820)
        at org.python.pycode._pyx0.f$0(testAttributeError.py)

Inspection of this code path shows:
  - the call of a __findattr__-method, which indicates a problem,
because the __findattr__ methods never throw AttributeError
  - org.python.core.PyInstance#ifindfunction(String) catches PyException
and returns null in case of an AttributeError. (In PyObject#__getattr__
 this null return value yields an AttributeError.)
 
I looked at the clients of PyInstance#ifindfunction(String). The method
is only used within PyInstance. In every case, we see the same pattern

  value=this.ifindfunction(name);
  ...
  if (value==null)
     noAttributeError(name);

Therefore it does not harm, to remove the try-catch clause from
ifindfunction. Of course we have to make sure, that the __findattr__
methods still return null instead of an AttributeError. Therefor I added
a new method PyInstance#__findattr_ex__(String name, boolean stopAtJava)
and changed PyInstance#__findattr__(String name, boolean stopAtJava) to
call the new method.

With the patch applied, the relevant parts of a stack trace look like this:

        at org.python.core.PyFunction.__call__(PyFunction.java:312)
        at org.python.core.PyInstance.ifindfunction(PyInstance.java:246)
        at org.python.core.PyInstance.__findattr_ex__(PyInstance.java:226)
        at org.python.core.PyInstance.__findattr_ex__(PyInstance.java:204)
        at org.python.core.PyObject.__getattr__(PyObject.java:820)
        at org.python.pycode._pyx0.f$0(testAttributeError.py)

No more __findattr__ methods. Looks good for me.
msg3441 (view) Author: Philip Jenvey (pjenvey) Date: 2008-08-19.22:24:22
reopening
msg3443 (view) Author: Leonardo Soto (leosoto) Date: 2008-08-21.03:24:34
Thanks! Patch applied on r5229, along with tests.
History
Date User Action Args
2008-08-21 03:24:35leosotosetstatus: open -> closed
resolution: accepted -> fixed
messages: + msg3443
2008-08-19 22:24:23pjenveysetstatus: closed -> open
resolution: fixed -> accepted
messages: + msg3441
nosy: + pjenvey
2008-08-19 11:58:38akruissetfiles: + 1095.diff
messages: + msg3439
2008-08-19 10:05:12akruissetmessages: + msg3438
2008-08-19 09:54:24akruissetmessages: + msg3436
2008-08-11 23:28:18leosotosetstatus: open -> closed
resolution: fixed
messages: + msg3425
2008-08-04 19:43:08leosotosetassignee: leosoto
messages: + msg3396
nosy: + leosoto
2008-08-04 10:12:53akruissetfiles: + testAttributeError.py
versions: + Jython 2.1, 2.2.2, 2.5alpha1
title: Attribute -> AttributeError raised from __getattr__ method are swallowed
messages: + msg3392
components: + Core
type: behaviour
2008-08-04 09:51:09akruiscreate