Well, it has been discussed at multiple times in the past and I have promised to write this PEP several times, so I finally found enough time to write a PEP on reorganizing exceptions for Python 3.0 .
Key points in this PEP is the reworking the hierarchy, requiring anything raised to inherit from a certain superclass, and to change bare 'except' clauses to catch a specific superclass. The first and last points I expect some contention, but the middle point I expect people are okay with (Guido liked the idea when Paul Prescod brought it up and the only person who didn't like it, Holger, ended up being okay with it when the superclass had a reasonable name). One thing people might not notice is that I have some minor ideas listed in the tree in parentheses. If people have an opinion on the ideas please speak up. Otherwise the other major points of contention are covered in the Open Issues section or will be brought up in the usual trashing of PEPs that cover contraversial changes. And please realize this is for Python 3.0! None of this is being proposed for any version before then (they could be, but that is another PEP entirely). Hopefully this PEP along with Ping's PEP 344 will cover the major ideas for exceptions for Python 3.0 . -Brett -------------------------------------------------------------- PEP: XXX Title: Exception Reorganization for Python 3.0 Version: $Revision: 1.5 $ Last-Modified: $Date: 2005/06/07 13:17:37 $ Author: Brett Cannon <[EMAIL PROTECTED]> Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 28-Jul-2005 Post-History: XX-XXX-XXX Abstract ======== Python, as of version 2.4, has 38 exceptions (including warnings) in the built-in namespace in a rather shallow hierarchy. This list of classes has grown over the years without a chance to learn from mistakes and cleaning up the hierarchy. This PEP proposes doing a reorganization for Python 3000 when backwards-compatibility is not an issue. It also proposes changing bare ``except`` clauses to catch only exceptions inheriting from a specific superclass. Lastly, this PEP proposes, in Python 3000, that all objects to be passed to a ``raise`` statement must inherit from a specific superclass. Rationale ========= Exceptions are a critical part of Python. While exceptions are traditionally used to signal errors in a program, they have also grown to be used for flow control for things such as iterators. There importance is great. But the organization of the exception hierarchy has not been maintained. Mostly for backwards-compatibility reasons, the hierarchy has stayed very flat and old exceptions who usefulness have not been proven have been left in. Making exceptions more hierarchical would help facilitate exception handling by making catching exceptions much more logical with use of superclasses. This should also help lead to less errors from being too broad in what exceptions are caught in an ``except`` clause. A required superclass for all exceptions is also being proposed [Summary2004-08-01]_. By requiring any object that is used in a ``raise`` statement to inherit from a specific superclass, certain attributes (such as those laid out in PEP 344 [PEP344]_) can be guaranteed to exist. This also will lead to the planned removal of string exceptions. Lastly, bare ``except`` clauses can be made less error-prone [Summary2004-09-01]_. Often people use a bare ``except`` when what they really wanted were non-critical exceptions to be caught while more system-specific ones, such as MemoryError, to pass through and to halt the interpreter. With a hierarchy reorganization, bare ``except`` clauses can be changed to only catch exceptions that subclass a non-critical exception superclass, allowing more critical exceptions to propagate to the top of the execution stack as was most likely intended. Philosophy of Reorganization ============================ There are several goals in this reorganization that defined the philosophy used to guide the work. One goal was to prune out unneeded exceptions. Extraneous exceptions should not be left in since it just serves to clutter the built-in namespace. Unneeded exceptions also dilute the importance of other exceptions by splitting uses between several exceptions when all uses should have been under a single exception. Another goal was to introduce any exceptions that were deemed needed to fill any holes in the hierarchy. Most new exceptions were done to flesh out the inheritance hierarchy to make it easier to catch a category of exceptions with a simpler ``except`` clause. Changing inheritance to make it more reasonable was a goal. As stated above, having proper inheritance allows for more accurate ``except`` statements when catching exceptions based on the inheritance tree. Lastly, any renaming to make an exception's use more obvious from its name was done. Having to look up what an exception is meant to be used for because the name does not proper reflect its usage is annoying and slows down debugging. Having a proper name also makes debugging easier on new programmers. New Hierarchy ============= Raisable (new; rename BaseException?) +-- CriticalException (new) +-- KeyboardInterrupt +-- MemoryError +-- SystemExit +-- SystemError (subclass SystemExit?) +-- AssertionError +-- SyntaxError +-- IndentationError +-- TabError +-- Exception (replaces StandardError) +-- ArithmeticError +-- FloatingPointError +-- DivideByZeroError +-- OverflowError +-- ControlFlowException (new) +-- StopIteration +-- GeneratorExit (introduced by PEP 342 [PEP342]_; subclass StopIteration?) +-- UnicodeError +-- UnicodeDecodeError +-- UnicodeEncodeError +-- UnicodeTranslateError (subclass UnicodeEncodeError and UnicodeDecodeError?) +-- LookupError (better name?) +-- IndexError +-- KeyError +-- TypeError +-- AttributeError (subclassing new) +-- OSError (does not inherit from EnvironmentError) +-- WindowsError +-- MacError (new) +-- UnixError (new) +-- IOError (does not inherit from EnvironmentError) +-- EOFError (subclassing new) +-- ImportError +-- NotImplementedError +-- NamespaceException (new) +-- UnboundGlobalError (new name for NameError) +-- UnboundLocalError (no longer subclasses UnboundGlobalError which replaces NameError) +-- WeakReferenceError (rename for ReferenceError) +-- ValueError +-- Warning +-- UserWarning +-- PendingDeprecationWarning +-- DeprecationWarning (subclassing new) +-- SyntaxWarning +-- SemanticsWarning (new name for RuntimeWarning) +-- FutureWarning Differences Compared to Python 2.4 ================================== Changes to exceptions from Python 2.4 can take shape in three forms: removal, renaming, or change in their superclass. There are also new exceptions introduced in the proposed hierarchy. New Exceptions -------------- Raisable '''''''' The base exception **all** exceptions inherit from. The name "Raisable" has been chosen to reflect that it is not meant to be treated as an exception directly, but as the common object that all things to base passed to ``raise`` must inherit from. CriticalException ''''''''''''''''' The superclass for exceptions for which a severe error has occurred that one would not want to catch accidentally. The name is meant to reflect the point that these exceptions are usually raised only when the interpreter should most likely be terminated. MacError '''''''' Introduced for symmertry with WindowsError. UnixError ''''''''' Introduced for symmetry with WindowsError. NamespaceException '''''''''''''''''' To provide a common superclass for exceptions dealing with namespace issues, this exception is introduced. Both UnboundLocalError and UnboundGlobalError (the new name for NameError) inherit from this class. Removed Exceptions ------------------ EnvironmentError '''''''''''''''' Originally meant as an exception for when an event outside of the interpreter needed to raise an exception, its use has been deemed unneeded. While both IOError and OSError inherited this class, the distinction between OS and I/O is significant enough to not warrant having a common subclass that one might base an ``except`` clause on. StandardError ''''''''''''' Originally meant to act as a superclass for exceptions not considered critical, its subclassing was rampant and partially negated its usefulness. The need for StandardError is negated thanks to the CriticalException/Exception dichotomy where Exception takes its place. RuntimeError '''''''''''' Meant to be used when an existing exception does not fit, its usefulness is consider meager in Python 2.4 [exceptionsmodule]_. Also, with the CriticalException/Exception dichotomy, Exception or CriticalException can be raised directly for the same use. Renamed Exceptions ------------------ ReferenceError '''''''''''''' Renamed WeakReferenceError. ReferenceError was added to the built-in exception hierarchy in Python 2.2 [exceptionsmodule]_. Taken directly from the ``weakref`` module, its name comes directly from its original name when it resided in the module. Unfortunately its name does not suggest its connection to weak references and thus deserves a renaming. NameError ''''''''' Renamed UnboundGlobalError. While NameError suggests its use, the name does not properly restrict its scope. With UnboundLocalError already in existence, it seems reasonable to change NameError to UnboundGlobalError to brings its name more in line. RuntimeWarning '''''''''''''' Renamed SemanticsWarning. RuntimeWarning is to represent semantic changes coming in the future. But while saying that affects "runtime" is true, flat-out stating it is a semantic change is much clearer, eliminating any possible association of "runtime" with the virtual machine specifically. Changed Inheritance ------------------- StopIteration and GeneratorExit ''''''''''''''''''''''''''''''' Inherit from ControlFlowException. Collecting all control flow-related exceptions under a common superclass continues with the theme of maintaining a hierarchy. AttributeError '''''''''''''' Inherits TypeError. Since most attribute access errors can be attributed to an object not being the type that one expects, it makes sense for AttributeError to be considered a type error. IOError ''''''' No longer subclasses EnvironmentError. While IOError does fall under the umbrella of EnvironmentError, the need for EnvironmentError has been deemed wanting, and thus has been removed. Thus IOError now subclasses Exception directly. EOFError '''''''' Subclasses IOError. Since an EOF comes from I/O it only makes sense that it be considered an I/O error. UnboundGlobalError and UnboundLocalError '''''''''''''''''''''''''''''''''''''''' Both subclass NamespaceException. Originally UnboundLocalError subclassed NameError (original name of UnboundGlobalError), but with both exceptions specifying a certain namespace, that subclassing has been deemed inappropriate. Instead, a common class between the two of them has been introduced. DeprecationWarning ''''''''''''''''''' Subclasses PendingDeprecationWarning. Since a DeprecationWarning is a PendingDeprecationWarning that is happening sooner, if you care about PendingDeprecationWarnings you are defintely going to care about DeprecationWarnings. Required Base Class for ``raise`` ================================= By requiring all objects passed to a ``raise`` statement, one is guaranteed that all exceptions will have certain attributes. If PEP 342 [PEP344]_ is accepted, the attributes outlined there will be guaranteed to be on all exceptions raised. This should help facilitate debugging by making the querying of information from exceptions much easier. Technically speaking, this can be easily implemented by modifying ``RAISE_VARARGS`` to do an inheritance check and raise TypeError if it does not inherit from Raisable. Bare ``except`` Clauses ======================= Bare ``except`` clauses typically are not meant to catch all exceptions. Often times catching KeyboardInterrupt is not intended. Unfortunately the exception hierarchy in Python 2.4 does not support a simple way to control what exceptions are caught except to be very explicit with a list of exceptions. The reorganization changes this by adding more of a hierarchy. If bare ``except`` statements only catch classes that inherit from Exception, its use will be more in line with what people expect. An implementation can be handled at the bytecode level by modifying the compiler to emit bytecode to do a check for Exception when a bare ``except`` is used. Open Issues =========== Change the Name of Raisable? ---------------------------- It has been argued that Raisable is a bad name to use since it is not an actual word [python-dev1]_. At issue is choosing a name that clearly explains the class' usage. "BaseException" might be acceptable although the name does not quite make it as explicit as Raisable that the class is not meant to be raised directly. Should Bare ``except`` Clauses be Removed? ------------------------------------------ It has been argued that bare ``except`` clauses should be removed entirely [python-dev2]_. The train of thought is that it will force people to specify what they want to catch and not be too broad. One issue with this, though, is whether people will make the proper choices of what to catch or go too broadly. Would a new Python programmer make the right decision and not catch CriticalExceptions or would they go overboard and catch Raisable? It would seem the issue boils down to whether we think people will make proper decisions or make a reasonable solution available for the simplest case assuming they might make a bad one. Change the name of Exception? ----------------------------- Some have suggested names other than "Exception" for the superclass to inherit from for bare ``except`` clauses to match against. The issue with going with a name that is not simplistic is that it raises the chance of people subclassing the wrong exception for what they want. By using the most generic name for the common case it raises the chances that people will use this class for subclassing their own exceptions rather than Raisable or CriticalException. Acknowledgements ================ XXX References ========== .. [PEP342] PEP 342 (Coroutines via Enhanced Generators) (http://www.python.org/peps/pep-0342.html) .. [PEP344] PEP 344 (Exception Chaining and Embedded Tracebacks) (http://www.python.org/peps/pep-0344.html) .. [exceptionsmodules] 'exceptions' module (http://docs.python.org/lib/module-exceptions.html) .. [Summary2004-08-01] python-dev Summary (An exception is an exception, unless it doesn't inherit from Exception) (http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception) .. [Summary2004-09-01] python-dev Summary (Cleaning the Exception House) (http://www.python.org/dev/summary/2004-09-01_2004-09-15.html#cleaning-the-exception-house) .. [python-dev1] python-dev email (Exception hierarchy) (http://mail.python.org/pipermail/python-dev/2004-August/047908.html) .. [python-dev2] python-dev email (Dangerous exceptions) (http://mail.python.org/pipermail/python-dev/2004-September/048681.html) Copyright ========= This document has been placed in the public domain. _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com