Issue 27823: Change bare AttributeError messages to be more informative (original) (raw)

Attached patch changes bare attribute errors to add a useful error message. As such, raise AttributeError and raise AttributeError(None) (but not raise AttributeError('') for example) inside any of the attribute lookup or descriptor methods (getattribute, getattr, setattr, delattr, get, set, and delete). This is a followup to #27794.

As such, the following is now much more useful and less boilerplate:

class A: _dynamic_names = {} def getattr(self, name): if name in self._dynamic_names: return self._dynamic_names[name] raise AttributeError

Then, doing the following:

A().spam

Will result in a AttributeError("'A' object has no attribute 'spam'"), while keeping the original traceback. The patch is pretty much complete, but I have a few concerns:

Oh, and exceptions set at the C level with e.g. PyErr_SetNone are also properly handled that way; this would fix #27794 as a side-effect, but a few lines will need to be changed (in Objects/descrobject.c) for this to work. Such changes are not included with the patch.

Thoughts, concerns, opinions, ideas? Maybe something I didn't think of?

Thanks for your comments Xiang. Yes, it's not equal to #27794, but it's one of the multiple ways to fix it, so I made a new issue about it.

The rationale between a bare raise AttributeError being changed is the idea that it carries no information, other than the fact there was an attribute error (it doesn't even mean the attribute doesn't exist, as it may). Also note that this only affects bare AttributeErrors inside of the magic methods dedicated to attribute lookup (and the descriptor machinery). In a regular function (or even just doing foo.__getattribute__(bar)), raise AttributeError does nothing special.

The argument "If you do want a good message, why not passing the message when raising?" is one I hear a lot when there's a suggestion to add a more convenient way to do something that can already be done. I've seen a lot of code (including code in the core Python distribution, both in Python and C) which lazily does raise AttributeError(name). It's only half the information, and these uninformative error messages are left completely untouched by my patch. The patch will help reduce boilerplate in some code. I'm personally one of those people who go ahead and actually use the built-in error message in my own code, but if I can reduce the boilerplate needed while maintaining the same level of information and usefulness for debugging, I'll be happy.

On the other hand, the argument that Python hides away some magic is a valid concern. Python already has some magic in various places; the point is to determine whether this kind of magic is acceptable for Python and its users. I honestly don't know; I don't think the magic is too strong, but at the same time it's likely to surprise newcomers. We'll see what others think :)