Update from Ethan.
This commit is contained in:
parent
f635b24076
commit
b8925c8605
128
pep-0409.txt
128
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
|
||||
|
|
Loading…
Reference in New Issue