[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:
do_stuff(item)
def do_stuff(item):
foo = item['bar']
do_other_stuff(foo)
# 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