classification
Title: Add a dedicated subclass for attribute missing errors
Type: enhancement Stage: resolved
Components: Interpreter Core Versions: Python 3.6
process
Status: closed Resolution: later
Dependencies: Superseder:
Assigned To: Nosy List: Jun Wang, ethan.furman, r.david.murray
Priority: normal Keywords:

Created on 2015-11-16 09:39 by Jun Wang, last changed 2015-11-28 15:32 by ethan.furman. This issue is now closed.

Messages (5)
msg254722 - (view) Author: Jun Wang (Jun Wang) Date: 2015-11-16 09:39
See this simple example:

class A(): 
	def __init__(self, x=None): 
		self.x = x

	@property
	def t(self): 
		return self.x.t

	def __getattr__(self, name): 
		return 'default'

print(A().t)


AttributeError is raised as "'NoneType' object has no attribute 't'". Currently __getattr__ is called if any AttributeError is raised, so the result of a.t is *default*, while an AttributeError is the desired behavior.

The most intuitive solution seems to add a subclass of AttributeError, say AttributeMissError, which triggers __getattr__. At present, I have to do some tricky and ugly things to __getattribute__ to show where the AttributeError occurs, or it's quite hard to figure out what happened with no informative traceback messages.
msg254735 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-11-16 14:50
I understand what you are concerned about, but unfortunately that would be a significant backward compatibility break.  This would need discussion on python-ideas, I think.  Even aside from the backward compatibility issue I have a feeling the additional complexity this would add to attribute resolution would be deemed too large to be worthwhile, but I haven't looked at the relevant code so I might be wrong.
msg254928 - (view) Author: Jun Wang (Jun Wang) Date: 2015-11-19 19:47
I think this is a common problem while using both __getattr__ and descriptor/property. A descriptor example:

class Descriptor(): 
	def __get__(self, instance, owner=None): 
		raise AttributeError('Implicitly suppressed')

class A(): 
	d = Descriptor()
	def __getattr__(self, name): 
		return 'default'

print(A().d)


Without descriptor, unexpected AttributeError could only come from overriding __getattribute__, which is a rare case, although still an imperfection. But in descriptor/property, AttributeError which is too general just occurs frequently like in normal method. 

Surely any modification would break the backward compatibility, although I wonder how often it is used of raising AttributeError purposely, maybe in __getattribute__, to call __getattr__, instead of explicitly calling __getattr__. In my understanding this is the only case that will be affected.


"An unexpected exception should not result in subtly altered behaviour, but should cause a noisy and easily-debugged traceback. "—from PEP479

About the implementation, maybe something like "RuntimeError: descriptor raised AttributeError" simulating PEP479. Or in my lay opinion, the best solution is: add object.__getattr__, with the only behavior of raising AttributeError; when normal attribute lookup fails, object.__getattribute__ calls __getattr__ explicitly; __getattr__ not triggered by AttributeError anymore.

I know little about the CPython implementation, so I might be completely wrong. However this seems deserving more detailed discussion.
msg254929 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-11-19 19:56
The more detailed discussion should happen on python-ideas.  I'm going to close this for now, but if there's a consensus there about what action to take this issue could be reopened.  (Or a new one created, whatever makes the most sense at that time.)
msg255543 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2015-11-28 15:32
Note for posterity:  the current behavior of __getattr__ is what allows Enum to work correctly.
History
Date User Action Args
2015-11-28 15:32:07ethan.furmansetnosy: + ethan.furman
messages: + msg255543
2015-11-19 19:56:57r.david.murraysetstatus: open -> closed
resolution: later
messages: + msg254929

stage: resolved
2015-11-19 19:47:33Jun Wangsetmessages: + msg254928
2015-11-16 14:50:13r.david.murraysetnosy: + r.david.murray
messages: + msg254735
2015-11-16 09:39:59Jun Wangcreate