[Python-ideas] Arguments to exceptions

Brendan Barnwell brenbarn at brenbarn.net
Wed Jul 5 14:34:44 EDT 2017

On 2017-07-05 04:36, Steven D'Aprano wrote:
> On Tue, Jul 04, 2017 at 11:37:51PM -0400, Terry Reedy wrote:
>> I personally been on the side of wanting richer exceptions.
> Could you explain what you would use them for? Ken has give two
> use-cases which I personally consider are relatively niche, and perhaps
> even counter-productive:
> - translation into the user's native language;
> - providing some sort of "did you mean...?" functionality.

	I'm not the one you were replying to, but I just consider it useful 
debugging information that reduces the work required to track down 
errors.  Suppose I have code like this:

for item in blah:

def do_stuff(item):
     foo = item['bar']

# etc., then 10 functions down the call stack. . .

def do_yet_additional_stuff(whatever):
      x = whatever['yadda']

	Now I get a KeyError exception in do_yet_additional_stuff.  If the 
object on which the key was not found ("whatever" here) is stored as an 
attribute of the exception, I can put a try/except in any function in 
the call stack and still have access to the object that caused the 
exception further down.  This makes it easier to try out hypotheses 
about what's causing the error.  ("Hmmm, I know that in do_stuff it 
reads the 'bar', attribute, maybe if that value is negative it's 
resulting in a NaN later on. . .")  If the object is not available as an 
exception attribute, I have to start by going all the way to the bottom 
of the call stack (to do_yet_additional_stuff) and either creating my 
own custom exception that does store the object (thus implementing this 
attribute-rich exception myself) or slowly working my way back up the 
call stack.  Having the object available on the exception is like having 
your finger in the pages of a book to hold the place where the exception 
actually occurred, while still being able to flip back and forth to 
other sections of the call stack to try to figure out how they're 
leading to that exception.

	A related use is when the exception is already caught and logged or 
handled somewhere higher up.  It makes it easier to log messages like 
"AttributeError was raised when foo had item=-10.345", or to 
conditionally handle exceptions based on such information, without 
requiring extra instrumentation elsewhere in the code.

	I realize this isn't a life-and-death use case, but to be frank I don't 
really see that the objections are that strong either.  So far I've seen 
"You don't really need that information", "You don't need that 
information if you're using exceptions for flow control" and "There 
might theoretically be a performance penalty of unknown significance". 
(I seem to recall seeing something like "exceptions aren't supposed to 
be debugging tools" in an earlier discussion about this, and I basically 
just disagree with that one.)

	Now, obviously there is the generic burden-of-proof argument that the 
suggestion doesn't meet the bar for changing the status quo, and that's 
as may be, but that's different from there being no use case.  It also 
doesn't necessarily rule out a potential resolution like "okay, this 
isn't urgent, but let's try to clean this up gradually as we move 
forward, by adding useful info to exceptions where possible".

	That said, I think this use case of mine is on a different track from 
where most of the discussion in this thread seems to have gone.  For one 
thing, I agree with you that NameError is a particularly odd exception 
to pick as the poster child for rich exceptions, because NameError is 
almost always raised in response to a variable name that's typed 
literally in the code, so it's much less likely to result in the kind of 
situation I described above, where the true cause of an exception is far 
away in the call stack from the point where it's raised.

	I also agree that it is much better to have the information available 
as named attributes on the exception rather than accessing them 
positionally with something like exception.args[1].  In fact I agree so 
much that basically what I'm saying is that all exceptions as much as 
possible should store relevant information in such attributes rather 
than putting it ONLY into the message string.

	Also, in the situation I'm describing, I'm not particularly attached to 
the idea that the "rich" information would even have to be part of the 
exception message at all by default (since the object might have a long 
__str__ that would be irritating).  It would just be there, attached to 
the exception, so that it could be used if needed.

Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."
    --author unknown

More information about the Python-ideas mailing list