Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| PEP: 352 | |
| Title: Required Superclass for Exceptions | |
| Version: $Revision$ | |
| Last-Modified: $Date$ | |
| Author: Brett Cannon, Guido van Rossum | |
| Status: Final | |
| Type: Standards Track | |
| Content-Type: text/x-rst | |
| Created: 27-Oct-2005 | |
| Post-History: | |
| Abstract | |
| ======== | |
| In Python 2.4 and before, any (classic) class can be raised as an | |
| exception. The plan for 2.5 was to allow new-style classes, but this | |
| makes the problem worse -- it would mean *any* class (or | |
| instance) can be raised! This is a problem as it prevents any | |
| guarantees from being made about the interface of exceptions. | |
| This PEP proposes introducing a new superclass that all raised objects | |
| must inherit from. Imposing the restriction will allow a standard | |
| interface for exceptions to exist that can be relied upon. It also | |
| leads to a known hierarchy for all exceptions to adhere to. | |
| One might counter that requiring a specific base class for a | |
| particular interface is unPythonic. However, in the specific case of | |
| exceptions there's a good reason (which has generally been agreed to | |
| on python-dev): requiring hierarchy helps code that wants to *catch* | |
| exceptions by making it possible to catch *all* exceptions explicitly | |
| by writing ``except BaseException:`` instead of | |
| ``except *:``. [#hierarchy-good]_ | |
| Introducing a new superclass for exceptions also gives us the chance | |
| to rearrange the exception hierarchy slightly for the better. As it | |
| currently stands, all exceptions in the built-in namespace inherit | |
| from Exception. This is a problem since this includes two exceptions | |
| (KeyboardInterrupt and SystemExit) that often need to be excepted from | |
| the application's exception handling: the default behavior of shutting | |
| the interpreter down without a traceback is usually more desirable than | |
| whatever the application might do (with the possible exception of | |
| applications that emulate Python's interactive command loop with | |
| ``>>>`` prompt). Changing it so that these two exceptions inherit | |
| from the common superclass instead of Exception will make it easy for | |
| people to write ``except`` clauses that are not overreaching and not | |
| catch exceptions that should propagate up. | |
| This PEP is based on previous work done for PEP 348 [#pep348]_. | |
| Requiring a Common Superclass | |
| ============================= | |
| This PEP proposes introducing a new exception named BaseException that | |
| is a new-style class and has a single attribute, ``args``. Below | |
| is the code as the exception will work in Python 3.0 (how it will | |
| work in Python 2.x is covered in the `Transition Plan`_ section):: | |
| class BaseException(object): | |
| """Superclass representing the base of the exception hierarchy. | |
| Provides an 'args' attribute that contains all arguments passed | |
| to the constructor. Suggested practice, though, is that only a | |
| single string argument be passed to the constructor. | |
| """ | |
| def __init__(self, *args): | |
| self.args = args | |
| def __str__(self): | |
| if len(self.args) == 1: | |
| return str(self.args[0]) | |
| else: | |
| return str(self.args) | |
| def __repr__(self): | |
| return "%s(*%s)" % (self.__class__.__name__, repr(self.args)) | |
| No restriction is placed upon what may be passed in for ``args`` | |
| for backwards-compatibility reasons. In practice, though, only | |
| a single string argument should be used. This keeps the string | |
| representation of the exception to be a useful message about the | |
| exception that is human-readable; this is why the ``__str__`` method | |
| special-cases on length-1 ``args`` value. Including programmatic | |
| information (e.g., an error code number) should be stored as a | |
| separate attribute in a subclass. | |
| The ``raise`` statement will be changed to require that any object | |
| passed to it must inherit from BaseException. This will make sure | |
| that all exceptions fall within a single hierarchy that is anchored at | |
| BaseException [#hierarchy-good]_. This also guarantees a basic | |
| interface that is inherited from BaseException. The change to | |
| ``raise`` will be enforced starting in Python 3.0 (see the `Transition | |
| Plan`_ below). | |
| With BaseException being the root of the exception hierarchy, | |
| Exception will now inherit from it. | |
| Exception Hierarchy Changes | |
| =========================== | |
| With the exception hierarchy now even more important since it has a | |
| basic root, a change to the existing hierarchy is called for. As it | |
| stands now, if one wants to catch all exceptions that signal an error | |
| *and* do not mean the interpreter should be allowed to exit, you must | |
| specify all but two exceptions specifically in an ``except`` clause | |
| or catch the two exceptions separately and then re-raise them and | |
| have all other exceptions fall through to a bare ``except`` clause:: | |
| except (KeyboardInterrupt, SystemExit): | |
| raise | |
| except: | |
| ... | |
| That is needlessly explicit. This PEP proposes moving | |
| KeyboardInterrupt and SystemExit to inherit directly from | |
| BaseException. | |
| :: | |
| - BaseException | |
| |- KeyboardInterrupt | |
| |- SystemExit | |
| |- Exception | |
| |- (all other current built-in exceptions) | |
| Doing this makes catching Exception more reasonable. It would catch | |
| only exceptions that signify errors. Exceptions that signal that the | |
| interpreter should exit will not be caught and thus be allowed to | |
| propagate up and allow the interpreter to terminate. | |
| KeyboardInterrupt has been moved since users typically expect an | |
| application to exit when they press the interrupt key (usually Ctrl-C). | |
| If people have overly broad ``except`` clauses the expected behaviour | |
| does not occur. | |
| SystemExit has been moved for similar reasons. Since the exception is | |
| raised when ``sys.exit()`` is called the interpreter should normally | |
| be allowed to terminate. Unfortunately overly broad ``except`` | |
| clauses can prevent the explicitly requested exit from occurring. | |
| To make sure that people catch Exception most of the time, various | |
| parts of the documentation and tutorials will need to be updated to | |
| strongly suggest that Exception be what programmers want to use. Bare | |
| ``except`` clauses or catching BaseException directly should be | |
| discouraged based on the fact that KeyboardInterrupt and SystemExit | |
| almost always should be allowed to propagate up. | |
| Transition Plan | |
| =============== | |
| Since semantic changes to Python are being proposed, a transition plan | |
| is needed. The goal is to end up with the new semantics being used in | |
| Python 3.0 while providing a smooth transition for 2.x code. All | |
| deprecations mentioned in the plan will lead to the removal of the | |
| semantics starting in the version following the initial deprecation. | |
| Here is BaseException as implemented in the 2.x series:: | |
| class BaseException(object): | |
| """Superclass representing the base of the exception hierarchy. | |
| The __getitem__ method is provided for backwards-compatibility | |
| and will be deprecated at some point. The 'message' attribute | |
| is also deprecated. | |
| """ | |
| def __init__(self, *args): | |
| self.args = args | |
| def __str__(self): | |
| return str(self.args[0] | |
| if len(self.args) <= 1 | |
| else self.args) | |
| def __repr__(self): | |
| func_args = repr(self.args) if self.args else "()" | |
| return self.__class__.__name__ + func_args | |
| def __getitem__(self, index): | |
| """Index into arguments passed in during instantiation. | |
| Provided for backwards-compatibility and will be | |
| deprecated. | |
| """ | |
| return self.args[index] | |
| def _get_message(self): | |
| """Method for 'message' property.""" | |
| warnings.warn("the 'message' attribute has been deprecated " | |
| "since Python 2.6") | |
| return self.args[0] if len(args) == 1 else '' | |
| message = property(_get_message, | |
| doc="access the 'message' attribute; " | |
| "deprecated and provided only for " | |
| "backwards-compatibility") | |
| Deprecation of features in Python 2.9 is optional. This is because it | |
| is not known at this time if Python 2.9 (which is slated to be the | |
| last version in the 2.x series) will actively deprecate features that | |
| will not be in 3.0. It is conceivable that no deprecation warnings | |
| will be used in 2.9 since there could be such a difference between 2.9 | |
| and 3.0 that it would make 2.9 too "noisy" in terms of warnings. Thus | |
| the proposed deprecation warnings for Python 2.9 will be revisited | |
| when development of that version begins, to determine if they are still | |
| desired. | |
| * Python 2.5 [done] | |
| - all standard exceptions become new-style classes [done] | |
| - introduce BaseException [done] | |
| - Exception, KeyboardInterrupt, and SystemExit inherit from | |
| BaseException [done] | |
| - deprecate raising string exceptions [done] | |
| * Python 2.6 [done] | |
| - deprecate catching string exceptions [done] | |
| - deprecate ``message`` attribute (see `Retracted Ideas`_) [done] | |
| * Python 2.7 [done] | |
| - deprecate raising exceptions that do not inherit from BaseException | |
| * Python 3.0 [done] | |
| - drop everything that was deprecated above: | |
| + string exceptions (both raising and catching) [done] | |
| + all exceptions must inherit from BaseException [done] | |
| + drop ``__getitem__``, ``message`` [done] | |
| Retracted Ideas | |
| =============== | |
| A previous version of this PEP that was implemented in Python 2.5 | |
| included a 'message' attribute on BaseException. Its purpose was to | |
| begin a transition to BaseException accepting only a single argument. | |
| This was to tighten the interface and to force people to use | |
| attributes in subclasses to carry arbitrary information with an | |
| exception instead of cramming it all into ``args``. | |
| Unfortunately, while implementing the removal of the ``args`` | |
| attribute in Python 3.0 at the PyCon 2007 sprint | |
| [#pycon2007-sprint-email]_, it was discovered that the transition was | |
| very painful, especially for C extension modules. It was decided that | |
| it would be better to deprecate the ``message`` attribute in | |
| Python 2.6 (and remove it in Python 2.7 and Python 3.0) and consider a | |
| more long-term transition strategy in Python 3.0 to remove | |
| multiple-argument support in BaseException in preference of accepting | |
| only a single argument. Thus the introduction of ``message`` and the | |
| original deprecation of ``args`` has been retracted. | |
| References | |
| ========== | |
| .. [#pep348] PEP 348 (Exception Reorganization for Python 3.0) | |
| http://www.python.org/dev/peps/pep-0348/ | |
| .. [#hierarchy-good] python-dev Summary for 2004-08-01 through 2004-08-15 | |
| 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 | |
| .. [#SF_1104669] SF patch #1104669 (new-style exceptions) | |
| https://bugs.python.org/issue1104669 | |
| .. [#pycon2007-sprint-email] python-3000 email ("How far to go with cleaning up exceptions") | |
| https://mail.python.org/pipermail/python-3000/2007-March/005911.html | |
| Copyright | |
| ========= | |
| This document has been placed in the public domain. | |
| .. | |
| Local Variables: | |
| mode: indented-text | |
| indent-tabs-mode: nil | |
| sentence-end-double-space: t | |
| fill-column: 70 | |
| End: |