classification
Title: Provide triggering AttributeError exception to __getattr__ for reraising
Type: enhancement Stage:
Components: Interpreter Core Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: nother, serhiy.storchaka, steven.daprano
Priority: normal Keywords:

Created on 2019-06-23 13:59 by nother, last changed 2019-06-23 20:11 by serhiy.storchaka.

Files
File name Uploaded Description Edit
test6.py nother, 2019-06-23 13:59 Example one function catching exception which is reraised by othe function called from within except branch
tes_attr1.py nother, 2019-06-23 19:22 Examples showing the problem and what would change.
Messages (4)
msg346323 - (view) Author: nother (nother) Date: 2019-06-23 13:59
Not sure where to file this suggestion properly and how.

Currently when mixing attributes and properties it can happen that an AttributeError not raised intentionally by @property getter can cause @property to vanish from from classinstance. This happens when on the class or any of its super classes __getattr__ method is defined and has no clue on how to handle attribute with name of property. In this case the latter is reported as not existent.

The understood purpose of this behavior is that this is the only means to enable @property getters to indicate the availability of the property for a specific class instance dependent upon local state of instance object. Another purpose is to implement canonical load/creation on deemand schema for @properties the same way as for attributes.

The down of this is that in case inside a more complex getter method an AttribteError is triggered unintentionally this also triggers call to __getattr__ whithout any means for the latter to figure whether the @property getter needs assistance or the error should be passed on unhandled.

Therefore i do suggest the following little change to slot_tp_getattr_hook method in file typeobject.c (github python/cpython master) on lines 6604 - 6607 from

`
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
        PyErr_Clear();
        res = call_attribute(self, getattr, name);
}
`

to 

`
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
        res = call_attribute(self, getattr, name);
        if ( res != NULL) {
             PyErr_Clear();
        }        
}
`

this little change would allow __getattr__ special method to 

  1) use plain raise (see pythoncode example attached) to reraise exception which caused the call to it

  2) allow @property getters convey by adding custom attribute to AttributteExcpetion  the reason for an intentionally raised AttributeError over to __getattr__ allowing for distinction between AttributeError raised to hide @property from class instance versus triggering load/creation on demmand for class instance vs AttributeError accidentially triggered by getter/setter/deleter code.


Still not possible would be for getattr and hasattr methods to figure whether the AttributeError is raised cause class instance does not have the requested attirbute or whether accessing the attribute or @property respekctive caused some there downstream an AttributeError. In case that would be reliably feasible at all.
msg346325 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2019-06-23 14:08
Could you supply a *simple* demonstration of a class showing this problem? I've looked at the uploaded "test6.py" (what happened to the other 5?) and don't see anything to do with either __getattr__ or property in it.
msg346336 - (view) Author: nother (nother) Date: 2019-06-23 19:22
Ah sorry, sure. Attached some examples hope they are not too artificially simple to still render real world scenarios.

The one loaded was just the test that a function called from within the except block inside another method can use raise keyword to reraise exception caught by caller.
msg346337 - (view) Author: nother (nother) Date: 2019-06-23 19:33
The first two exampel should produce the same result not throwing any exception. The third example should as it does report that prop is not available for instance.

The fourth example should pass on the AttributeError raised for the not available attr2 instead of raising a new one stating the prop2 is not available form instance.

In all cases the change would allow __getattr__ instead of raising a new AttributeError exception reraise the one triggering the  __getattr__ call
History
Date User Action Args
2019-06-23 20:11:33serhiy.storchakasetnosy: + serhiy.storchaka
2019-06-23 19:33:46nothersetmessages: + msg346337
2019-06-23 19:22:58nothersetfiles: + tes_attr1.py

messages: + msg346336
2019-06-23 14:08:13steven.dapranosetnosy: + steven.daprano

messages: + msg346325
versions: + Python 3.9
2019-06-23 13:59:44nothercreate