uncaught exceptions

Doug Moen (dmoen@io.org)
Fri, 17 Dec 93 08:26 WET

> Jaap Vermeulen:
> >| like a standard Unix error message, after the fashion of perror(3)? If you
> >| want it to send out the full debugging stack trace, perhaps a command-line
> >| arg could restore the current behavior? Maybe -D?

> >Guido's argument has always been that stacktraces result because of
> >programming errors and it helps tracking them down. I tend to agree and find
> >it dangerous to start using a shortcut resulting in cryptic UNIX messages.
> >My complaint in the past has been that the stacktrace scares or annoys an
> >ordinary user, since he/she doesn't know what to do next.

Bennett Todd:
> I can't disagree very hard; the current behavior of Python is extremely
> useful. What's more, Jaap's boilerplate is only 23 lines long, so it isn't
> too great a trial to have to hand-include something like it in every program
> I ever write.
>
> Nonetheless, I don't think the current default behavior is entirely suitable
> for final delivered programs. If a user tries to cat(1) a file that doesn't
> exist, they don't really want to see a stack backtrace; they want to see a
> perror(3) with the name of the offending file, and what's wrong.

History repeats itself. A few years ago, I wrote an exception handling
package for C; this was used by a group of 8-9 C programmers to write a
system containing about 200,000 lines of code. The same debate occurred,
and initially, I took the same position as Guido.

Originally, my exception package forced a core dump in response to an
uncaught exception. My position was that an uncaught exception indicates
a bug in the program, and a core dump is appropriate because it contains
the stack trace (and other contextual info) you need to find the bug.
My users disagreed. They wanted an uncaught exception to (by default)
exit with a Unix-style error message, appropriate for viewing by end-users.
Core dumps were deemed inappropriate, because they scare & annoy end users.

In the final system, there were two categories of errors: faults and failures.
A fault always indicates a bug in the program, and always results in a core
dump. A failure is an "abnormal but anticipated condition", and results in
an exception. Uncaught exceptions exit with a user-friendly Unix style
message. You could set an environment variable to force a core dump instead;
I used this a lot, but I'm not sure if many other people did. It was also
trivial to insert a little bit of boiler plate into your program to force
a core dump on all 'uncaught' exceptions; no one ever did that, either.

By analogy, Python could be changed to print a relatively short message
by default when an uncaught exception occurs, and the onus could be on the
programmer to do some extra work (like setting a Python variable, an
environment variable or including boilerplate) to get verbose, multipage
stack traces instead. I think it depends on what's most useful to the
majority of Python programmers.

Bennett:
> But now I see the big defect in my idea; the current behavior is absolutely
> appropriate for ``can't happen'' problems, i.e. programmer errors.

So Bennett wants Python to classify exceptions into 2 categories, and take a
different default action on uncaught exceptions, depending on the category.
My system did, in fact, classify exceptions. In Python, an error name is
bound to a string which names the error. In my system, an error name was
bound to a structure containing:
an error name
a severity level (a simple classification system)
a short, one line message, analogous to the messages in sys_errlist
a verbose, one paragraph description of the error

Grouping collections of related errors into categories is a powerful idea,
particularly if you can write exception handlers that catch all errors
belonging to a particular category, rather than writing an exhaustive list
of error names. Eric Allman wrote an exception system in which error names
are strings, and error handlers can use glob-style pattern matching to
identify categories of exceptions. In Common Lisp, an error name is bound
to a subclass of Condition (rather than a string), and an error value is
an instance of this class (rather than a name, value pair). Errors are
grouped into categories using inheritance, and an error can belong to several
disjoint categories by using multiple inheritance. An error handler
specifies the classes of the conditions it wants to catch.

But I digress. You don't actually need an error category associated with
the error itself to provide what Bennett is asking for. You can, instead,
have two commands for raising exceptions. In my system, you call 'fatal'
when you detect a bug, and you call 'throw_x' when you detect a failure
condition. The first causes a core dump, the second exits with a user
friendly message if it isn't caught. Likewise, in Common Lisp, you call
'error' when you detect a serious condition (by default, this enters the
debugger), and you call 'signal' when you detect a non-serious condition.

Bibliography
Eric Allman and David Been
"An Exception Handler for C"
Proceedings of the Summer 1985 USENIX Conference

Doug Moen
"A Discipline of Error Handling"
Proceedings of the Summer '92 USENIX Conference

Guy L. Steele Jr.
Common Lisp, The Language