diff --git a/pep-0409.txt b/pep-0409.txt index 3347bb67d..9dcbb52d4 100644 --- a/pep-0409.txt +++ b/pep-0409.txt @@ -7,8 +7,7 @@ Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 26-Jan-2012 -Python-Version: 3.3 -Post-History: 2012-01-27 +Post-History: 30-Aug-2002, 01-Feb-2012, 03-Feb-2012 Abstract @@ -18,48 +17,50 @@ One of the open issues from PEP 3134 is suppressing context: currently there is no way to do it. This PEP proposes one. -Motivation -========== +Rationale +========= -There are two basic ways to generate exceptions: 1) Python does it -(buggy code, missing resources, ending loops, etc.); and, 2) manually -(with a raise statement). +There are two basic ways to generate exceptions: + +1. Python does it (buggy code, missing resources, ending loops, etc.) + +2. manually (with a raise statement) When writing libraries, or even just custom classes, it can become necessary to raise exceptions; moreover it can be useful, even necessary, to change from one exception to another. To take an example from my dbf module:: - try: - value = int(value) - except Exception: - raise DbfError(...) + try: + value = int(value) + except Exception: + raise DbfError(...) -Whatever the original exception was (ValueError, TypeError, or +Whatever the original exception was (``ValueError``, ``TypeError``, or something else) is irrelevant. The exception from this point on is a -DbfError, and the original exception is of no value. However, if this -exception is printed, we would currently see both. +``DbfError``, and the original exception is of no value. However, if +this exception is printed, we would currently see both. Alternatives ============ - Several possibilities have been put forth: -- ``raise as NewException()`` +* ``raise as NewException()`` - Reuses the 'as' keyword; can be confusing since we are not really reraising - the originating exception + Reuses the ``as`` keyword; can be confusing since we are not really + reraising the originating exception -- ``raise NewException() from None`` +* ``raise NewException() from None`` - Follows existing syntax of explicitly declaring the originating exception + Follows existing syntax of explicitly declaring the originating + exception -- ``exc = NewException(); exc.__context__ = None; raise exc`` +* ``exc = NewException(); exc.__context__ = None; raise exc`` Very verbose way of the previous method -- ``raise NewException.no_context(...)`` +* ``raise NewException.no_context(...)`` Make context suppression a class method. @@ -78,15 +79,91 @@ setting the cause:: raise KeyError() from NameError() -but because the 'cause' is None the previous context is discarded. -There is a patch to this effect attached to issue 6210 [#issue6210]_. +but because the cause is ``None`` the previous context is not +displayed by the default exception printing routines. + + +Implementation Discussion +========================= + +Currently, ``None`` is the default for both ``__context__`` and +``__cause__``. In order to support ``raise ... from None`` (which +would set ``__cause__`` to ``None``) we need a different default value +for ``__cause__``. Several ideas were put forth on how to implement +this at the language level: + +* Overwrite the previous exception information (side-stepping the + issue and leaving ``__cause__`` at ``None``). + + Rejected as this can seriously hinder debugging due to `poor error + messages`_. + +* Use one of the boolean values in ``__cause__``: ``False`` would be + the default value, and would be replaced when ``from ...`` was used + with the explicity chained exception or ``None``. + + Rejected as this encourages the use of two different objects types + for ``__cause__`` with one of them (boolean) not allowed to have the + full range of possible values (``True`` would never be used). + +* Create a special exception class, ``__NoException__``. + + Rejected as possibly confusing, possibly being mistakenly raised by + users, and not being a truly unique value as ``None``, ``True``, and + ``False`` are. + +* Use ``Ellipsis`` as the default value (the ``...`` singleton). + + Accepted. There are no other possible values; it cannot be raised + as it is not an exception; it has the connotation of 'fill in the + rest...' as in ``__cause__`` is not set, look in ``__context__`` for + it. + + +Language Details +================ + +To support ``from None``, ``__context__`` will stay as it is, but +``__cause__`` will start out as ``Ellipsis`` and will change to +``None`` when the ``raise ... from None`` method is used. + +============================================ ================== ======================================= +form __context__ __cause__ +============================================ ================== ======================================= +raise ``None`` ``Ellipsis`` +reraise previous exception ``Ellipsis`` +reraise from ``None`` | ``ChainedException`` previous exception ``None`` | explicitly chained exception +============================================ ================== ======================================= + +The default exception printing routine will then: + +* If ``__cause__`` is ``Ellipsis`` the ``__context__`` (if any) will + be printed. + +* If ``__cause__`` is ``None`` the ``__context__`` will not be + printed. + +* If ``__cause__`` is anything else, ``__cause__`` will be printed. + + +Patches +======= + +There is a patch for CPython implementing this attached to `Issue +6210`_. References ========== -.. [#issue6210] +Discussion and refinements in this `thread on python-dev`_. + +.. _poor error messages: + http://bugs.python.org/msg152294 +.. _issue 6210: http://bugs.python.org/issue6210 +.. _Thread on python-dev: + http://mail.python.org/pipermail/python-dev/2012-January/115838.html Copyright @@ -95,7 +172,6 @@ Copyright This document has been placed in the public domain. - .. Local Variables: mode: indented-text