Latest update from Ethan, marking PEP 409 as accepted.

This commit is contained in:
Brett Cannon 2012-02-05 19:36:12 -05:00
parent 5b1a3f5b4e
commit 7506ff282f
1 changed files with 47 additions and 42 deletions

View File

@ -3,11 +3,12 @@ Title: Suppressing exception context
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Ethan Furman <ethan@stoneleaf.us> Author: Ethan Furman <ethan@stoneleaf.us>
Status: Draft Status: Accepted
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst Content-Type: text/x-rst
Created: 26-Jan-2012 Created: 26-Jan-2012
Post-History: 30-Aug-2002, 01-Feb-2012, 03-Feb-2012 Post-History: 30-Aug-2002, 01-Feb-2012, 03-Feb-2012
Resolution: http://mail.python.org/pipermail/python-dev/2012-February/116136.html
Abstract Abstract
@ -22,14 +23,14 @@ Rationale
There are two basic ways to generate exceptions: There are two basic ways to generate exceptions:
1. Python does it (buggy code, missing resources, ending loops, etc.) 1) Python does it (buggy code, missing resources, ending loops, etc.)
2. manually (with a raise statement) 2) manually (with a raise statement)
When writing libraries, or even just custom classes, it can become When writing libraries, or even just custom classes, it can become
necessary to raise exceptions; moreover it can be useful, even necessary to raise exceptions; moreover it can be useful, even
necessary, to change from one exception to another. To take an necessary, to change from one exception to another. To take an example
example from my dbf module:: from my dbf module::
try: try:
value = int(value) value = int(value)
@ -74,58 +75,55 @@ I proprose going with the second option::
raise NewException from None raise NewException from None
It has the advantage of using the existing pattern of explicitly It has the advantage of using the existing pattern of explicitly setting
setting the cause:: the cause::
raise KeyError() from NameError() raise KeyError() from NameError()
but because the cause is ``None`` the previous context is not but because the cause is ``None`` the previous context is not displayed
displayed by the default exception printing routines. by the default exception printing routines.
Implementation Discussion Implementation Discussion
========================= =========================
Currently, ``None`` is the default for both ``__context__`` and Currently, ``None`` is the default for both ``__context__`` and ``__cause__``.
``__cause__``. In order to support ``raise ... from None`` (which In order to support ``raise ... from None`` (which would set ``__cause__`` to
would set ``__cause__`` to ``None``) we need a different default value ``None``) we need a different default value for ``__cause__``. Several ideas
for ``__cause__``. Several ideas were put forth on how to implement were put forth on how to implement this at the language level:
this at the language level:
* Overwrite the previous exception information (side-stepping the * Overwrite the previous exception information (side-stepping the issue and
issue and leaving ``__cause__`` at ``None``). leaving ``__cause__`` at ``None``).
Rejected as this can seriously hinder debugging due to `poor error Rejected as this can seriously hinder debugging due to
messages`_. `poor error messages`_.
* Use one of the boolean values in ``__cause__``: ``False`` would be * Use one of the boolean values in ``__cause__``: ``False`` would be the
the default value, and would be replaced when ``from ...`` was used default value, and would be replaced when ``from ...`` was used with the
with the explicity chained exception or ``None``. explicity chained exception or ``None``.
Rejected as this encourages the use of two different objects types Rejected as this encourages the use of two different objects types for
for ``__cause__`` with one of them (boolean) not allowed to have the ``__cause__`` with one of them (boolean) not allowed to have the full range
full range of possible values (``True`` would never be used). of possible values (``True`` would never be used).
* Create a special exception class, ``__NoException__``. * Create a special exception class, ``__NoException__``.
Rejected as possibly confusing, possibly being mistakenly raised by Rejected as possibly confusing, possibly being mistakenly raised by users,
users, and not being a truly unique value as ``None``, ``True``, and and not being a truly unique value as ``None``, ``True``, and ``False`` are.
``False`` are.
* Use ``Ellipsis`` as the default value (the ``...`` singleton). * Use ``Ellipsis`` as the default value (the ``...`` singleton).
Accepted. There are no other possible values; it cannot be raised Accepted. There are no other possible values; it cannot be raised as it is
as it is not an exception; it has the connotation of 'fill in the not an exception; it has the connotation of 'fill in the rest...' as in
rest...' as in ``__cause__`` is not set, look in ``__context__`` for ``__cause__`` is not set, look in ``__context__`` for it.
it.
Language Details Language Details
================ ================
To support ``from None``, ``__context__`` will stay as it is, but To support ``from None``, ``__context__`` will stay as it is, but
``__cause__`` will start out as ``Ellipsis`` and will change to ``__cause__`` will start out as ``Ellipsis`` and will change to ``None``
``None`` when the ``raise ... from None`` method is used. when the ``raise ... from None`` method is used.
============================================ ================== ======================================= ============================================ ================== =======================================
form __context__ __cause__ form __context__ __cause__
@ -137,20 +135,26 @@ reraise from ``None`` | ``ChainedException`` previous exception ``None`` | exp
The default exception printing routine will then: The default exception printing routine will then:
* If ``__cause__`` is ``Ellipsis`` the ``__context__`` (if any) will * If ``__cause__`` is ``Ellipsis`` the ``__context__`` (if any) will be
be printed.
* If ``__cause__`` is ``None`` the ``__context__`` will not be
printed. printed.
* If ``__cause__`` is anything else, ``__cause__`` will be printed. * If ``__cause__`` is ``None`` the ``__context__`` will not be printed.
* if ``__cause__`` is anything else, ``__cause__`` will be printed.
In both of the latter cases the exception chain will stop being followed.
Because the default value for ``__cause__`` is now ``Ellipsis``::
raise Exception from Ellipsis
is allowed and will (re)set ``__cause__`` to ``Ellipsis``.
Patches Patches
======= =======
There is a patch for CPython implementing this attached to `Issue There is a patch for CPython implementing this attached to `Issue 6210`_.
6210`_.
References References
@ -171,7 +175,7 @@ Copyright
This document has been placed in the public domain. This document has been placed in the public domain.
.. ..
Local Variables: Local Variables:
mode: indented-text mode: indented-text
@ -180,3 +184,4 @@ This document has been placed in the public domain.
fill-column: 70 fill-column: 70
coding: utf-8 coding: utf-8
End: End: