reSTify 5 PEPs (#276)
This commit is contained in:
parent
02969ebbd2
commit
329ed7e935
472
pep-0236.txt
472
pep-0236.txt
|
@ -5,369 +5,375 @@ Last-Modified: $Date$
|
|||
Author: Tim Peters <tim.peters@gmail.com>
|
||||
Status: Final
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 26-Feb-2001
|
||||
Python-Version: 2.1
|
||||
Post-History: 26-Feb-2001
|
||||
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
From time to time, Python makes an incompatible change to the
|
||||
advertised semantics of core language constructs, or changes their
|
||||
accidental (implementation-dependent) behavior in some way. While this
|
||||
is never done capriciously, and is always done with the aim of
|
||||
improving the language over the long term, over the short term it's
|
||||
contentious and disrupting.
|
||||
From time to time, Python makes an incompatible change to the advertised
|
||||
semantics of core language constructs, or changes their accidental
|
||||
(implementation-dependent) behavior in some way. While this is never done
|
||||
capriciously, and is always done with the aim of improving the language over
|
||||
the long term, over the short term it's contentious and disrupting.
|
||||
|
||||
PEP 5, Guidelines for Language Evolution[1] suggests ways to ease
|
||||
the pain, and this PEP introduces some machinery in support of that.
|
||||
PEP 5, Guidelines for Language Evolution [1]_ suggests ways to ease the pain,
|
||||
and this PEP introduces some machinery in support of that.
|
||||
|
||||
PEP 227, Statically Nested Scopes[2] is the first application, and
|
||||
will be used as an example here.
|
||||
PEP 227, Statically Nested Scopes [2]_ is the first application, and will be
|
||||
used as an example here.
|
||||
|
||||
|
||||
Intent
|
||||
======
|
||||
|
||||
[Note: This is policy, and so should eventually move into PEP 5 [1]]
|
||||
[Note: This is policy, and so should eventually move into PEP 5 [1]_]
|
||||
|
||||
When an incompatible change to core language syntax or semantics is
|
||||
being made:
|
||||
When an incompatible change to core language syntax or semantics is being
|
||||
made:
|
||||
|
||||
1. The release C that introduces the change does not change the
|
||||
syntax or semantics by default.
|
||||
1. The release C that introduces the change does not change the syntax or
|
||||
semantics by default.
|
||||
|
||||
2. A future release R is identified in which the new syntax or semantics
|
||||
will be enforced.
|
||||
2. A future release R is identified in which the new syntax or semantics will
|
||||
be enforced.
|
||||
|
||||
3. The mechanisms described in PEP 3, Warning Framework[3] are
|
||||
used to generate warnings, whenever possible, about constructs
|
||||
or operations whose meaning may[4] change in release R.
|
||||
3. The mechanisms described in PEP 3, Warning Framework [3]_ are used to
|
||||
generate warnings, whenever possible, about constructs or operations whose
|
||||
meaning may [4]_ change in release R.
|
||||
|
||||
4. The new future_statement (see below) can be explicitly included in a
|
||||
module M to request that the code in module M use the new syntax or
|
||||
semantics in the current release C.
|
||||
4. The new future_statement (see below) can be explicitly included in a module
|
||||
M to request that the code in module M use the new syntax or semantics in
|
||||
the current release C.
|
||||
|
||||
So old code continues to work by default, for at least one release,
|
||||
although it may start to generate new warning messages. Migration to
|
||||
the new syntax or semantics can proceed during that time, using the
|
||||
future_statement to make modules containing it act as if the new syntax
|
||||
or semantics were already being enforced.
|
||||
So old code continues to work by default, for at least one release, although
|
||||
it may start to generate new warning messages. Migration to the new syntax or
|
||||
semantics can proceed during that time, using the future_statement to make
|
||||
modules containing it act as if the new syntax or semantics were already being
|
||||
enforced.
|
||||
|
||||
Note that there is no need to involve the future_statement machinery
|
||||
in new features unless they can break existing code; fully backward-
|
||||
compatible additions can-- and should --be introduced without a
|
||||
corresponding future_statement.
|
||||
Note that there is no need to involve the future_statement machinery in new
|
||||
features unless they can break existing code; fully backward- compatible
|
||||
additions can-- and should --be introduced without a corresponding
|
||||
future_statement.
|
||||
|
||||
|
||||
Syntax
|
||||
======
|
||||
|
||||
A future_statement is simply a from/import statement using the reserved
|
||||
module name __future__:
|
||||
A future_statement is simply a from/import statement using the reserved module
|
||||
name ``__future__``::
|
||||
|
||||
future_statement: "from" "__future__" "import" feature ["as" name]
|
||||
("," feature ["as" name])*
|
||||
future_statement: "from" "__future__" "import" feature ["as" name]
|
||||
(","feature ["as" name])*
|
||||
|
||||
feature: identifier
|
||||
name: identifier
|
||||
feature: identifier
|
||||
name: identifier
|
||||
|
||||
In addition, all future_statments must appear near the top of the
|
||||
module. The only lines that can appear before a future_statement are:
|
||||
In addition, all future_statments must appear near the top of the module. The
|
||||
only lines that can appear before a future_statement are:
|
||||
|
||||
+ The module docstring (if any).
|
||||
+ Comments.
|
||||
+ Blank lines.
|
||||
+ Other future_statements.
|
||||
+ The module docstring (if any).
|
||||
+ Comments.
|
||||
+ Blank lines.
|
||||
+ Other future_statements.
|
||||
|
||||
Example:
|
||||
"""This is a module docstring."""
|
||||
Example::
|
||||
|
||||
# This is a comment, preceded by a blank line and followed by
|
||||
# a future_statement.
|
||||
from __future__ import nested_scopes
|
||||
"""This is a module docstring."""
|
||||
|
||||
from math import sin
|
||||
from __future__ import alabaster_weenoblobs # compile-time error!
|
||||
# That was an error because preceded by a non-future_statement.
|
||||
# This is a comment, preceded by a blank line and followed by
|
||||
# a future_statement.
|
||||
from __future__ import nested_scopes
|
||||
|
||||
from math import sin
|
||||
from __future__ import alabaster_weenoblobs # compile-time error!
|
||||
# That was an error because preceded by a non-future_statement.
|
||||
|
||||
|
||||
Semantics
|
||||
=========
|
||||
|
||||
A future_statement is recognized and treated specially at compile time:
|
||||
changes to the semantics of core constructs are often implemented by
|
||||
generating different code. It may even be the case that a new feature
|
||||
introduces new incompatible syntax (such as a new reserved word), in
|
||||
which case the compiler may need to parse the module differently. Such
|
||||
decisions cannot be pushed off until runtime.
|
||||
A future_statement is recognized and treated specially at compile time:
|
||||
changes to the semantics of core constructs are often implemented by
|
||||
generating different code. It may even be the case that a new feature
|
||||
introduces new incompatible syntax (such as a new reserved word), in which
|
||||
case the compiler may need to parse the module differently. Such decisions
|
||||
cannot be pushed off until runtime.
|
||||
|
||||
For any given release, the compiler knows which feature names have been
|
||||
defined, and raises a compile-time error if a future_statement contains
|
||||
a feature not known to it[5].
|
||||
For any given release, the compiler knows which feature names have been
|
||||
defined, and raises a compile-time error if a future_statement contains a
|
||||
feature not known to it [5]_.
|
||||
|
||||
The direct runtime semantics are the same as for any import statement:
|
||||
there is a standard module __future__.py, described later, and it will
|
||||
be imported in the usual way at the time the future_statement is
|
||||
executed.
|
||||
The direct runtime semantics are the same as for any ``import`` statement:
|
||||
there is a standard module ``__future__.py``, described later, and it will be
|
||||
imported in the usual way at the time the future_statement is executed.
|
||||
|
||||
The *interesting* runtime semantics depend on the specific feature(s)
|
||||
"imported" by the future_statement(s) appearing in the module.
|
||||
The *interesting* runtime semantics depend on the specific feature(s)
|
||||
"imported" by the future_statement(s) appearing in the module.
|
||||
|
||||
Note that there is nothing special about the statement:
|
||||
Note that there is nothing special about the statement::
|
||||
|
||||
import __future__ [as name]
|
||||
import __future__ [as name]
|
||||
|
||||
That is not a future_statement; it's an ordinary import statement, with
|
||||
no special semantics or syntax restrictions.
|
||||
That is not a future_statement; it's an ordinary import statement, with no
|
||||
special semantics or syntax restrictions.
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Consider this code, in file scope.py:
|
||||
Consider this code, in file scope.py::
|
||||
|
||||
x = 42
|
||||
def f():
|
||||
x = 666
|
||||
def g():
|
||||
print "x is", x
|
||||
g()
|
||||
f()
|
||||
x = 42
|
||||
def f():
|
||||
x = 666
|
||||
def g():
|
||||
print "x is", x
|
||||
g()
|
||||
f()
|
||||
|
||||
Under 2.0, it prints:
|
||||
Under 2.0, it prints::
|
||||
|
||||
x is 42
|
||||
x is 42
|
||||
|
||||
Nested scopes[2] are being introduced in 2.1. But under 2.1, it still
|
||||
prints
|
||||
Nested scopes [2]_ are being introduced in 2.1. But under 2.1, it still
|
||||
prints::
|
||||
|
||||
x is 42
|
||||
x is 42
|
||||
|
||||
and also generates a warning.
|
||||
and also generates a warning.
|
||||
|
||||
In 2.2, and also in 2.1 *if* "from __future__ import nested_scopes" is
|
||||
included at the top of scope.py, it prints
|
||||
In 2.2, and also in 2.1 *if* ``from __future__ import nested_scopes`` is
|
||||
included at the top of ``scope.py``, it prints::
|
||||
|
||||
x is 666
|
||||
x is 666
|
||||
|
||||
|
||||
Standard Module __future__.py
|
||||
=============================
|
||||
|
||||
Lib/__future__.py is a real module, and serves three purposes:
|
||||
``Lib/__future__.py`` is a real module, and serves three purposes:
|
||||
|
||||
1. To avoid confusing existing tools that analyze import statements and
|
||||
expect to find the modules they're importing.
|
||||
1. To avoid confusing existing tools that analyze import statements and expect
|
||||
to find the modules they're importing.
|
||||
|
||||
2. To ensure that future_statements run under releases prior to 2.1
|
||||
at least yield runtime exceptions (the import of __future__ will
|
||||
fail, because there was no module of that name prior to 2.1).
|
||||
2. To ensure that future_statements run under releases prior to 2.1 at least
|
||||
yield runtime exceptions (the import of ``__future__`` will fail, because
|
||||
there was no module of that name prior to 2.1).
|
||||
|
||||
3. To document when incompatible changes were introduced, and when they
|
||||
will be-- or were --made mandatory. This is a form of executable
|
||||
documentation, and can be inspected programatically via importing
|
||||
__future__ and examining its contents.
|
||||
3. To document when incompatible changes were introduced, and when they will
|
||||
be-- or were --made mandatory. This is a form of executable documentation,
|
||||
and can be inspected programatically via importing ``__future__`` and
|
||||
examining its contents.
|
||||
|
||||
Each statement in __future__.py is of the form:
|
||||
Each statement in ``__future__.py`` is of the form::
|
||||
|
||||
FeatureName = "_Feature(" OptionalRelease "," MandatoryRelease ")"
|
||||
FeatureName = "_Feature(" OptionalRelease "," MandatoryRelease ")"
|
||||
|
||||
where, normally, OptionalRelease < MandatoryRelease, and both are
|
||||
5-tuples of the same form as sys.version_info:
|
||||
where, normally, *OptionalRelease* < *MandatoryRelease*, and both are
|
||||
5-tuples of the same form as ``sys.version_info``::
|
||||
|
||||
(PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int
|
||||
PY_MINOR_VERSION, # the 1; an int
|
||||
PY_MICRO_VERSION, # the 0; an int
|
||||
PY_RELEASE_LEVEL, # "alpha", "beta", "candidate" or "final"; string
|
||||
PY_RELEASE_SERIAL # the 3; an int
|
||||
)
|
||||
PY_RELEASE_SERIAL # the 3; an int )
|
||||
|
||||
OptionalRelease records the first release in which
|
||||
*OptionalRelease* records the first release in which::
|
||||
|
||||
from __future__ import FeatureName
|
||||
from __future__ import FeatureName
|
||||
|
||||
was accepted.
|
||||
was accepted.
|
||||
|
||||
In the case of MandatoryReleases that have not yet occurred,
|
||||
MandatoryRelease predicts the release in which the feature will become
|
||||
part of the language.
|
||||
In the case of *MandatoryReleases* that have not yet occurred,
|
||||
*MandatoryRelease* predicts the release in which the feature will become part
|
||||
of the language.
|
||||
|
||||
Else MandatoryRelease records when the feature became part of the
|
||||
language; in releases at or after that, modules no longer need
|
||||
Else *MandatoryRelease* records when the feature became part of the language;
|
||||
in releases at or after that, modules no longer need::
|
||||
|
||||
from __future__ import FeatureName
|
||||
from __future__ import FeatureName
|
||||
|
||||
to use the feature in question, but may continue to use such imports.
|
||||
to use the feature in question, but may continue to use such imports.
|
||||
|
||||
MandatoryRelease may also be None, meaning that a planned feature got
|
||||
dropped.
|
||||
*MandatoryRelease* may also be ``None``, meaning that a planned feature got
|
||||
dropped.
|
||||
|
||||
Instances of class _Feature have two corresponding methods,
|
||||
.getOptionalRelease() and .getMandatoryRelease().
|
||||
Instances of ``class _Feature`` have two corresponding methods,
|
||||
``.getOptionalRelease()`` and ``.getMandatoryRelease()``.
|
||||
|
||||
No feature line will ever be deleted from __future__.py.
|
||||
No feature line will ever be deleted from ``__future__.py``.
|
||||
|
||||
Example line:
|
||||
Example line::
|
||||
|
||||
nested_scopes = _Feature((2, 1, 0, "beta", 1), (2, 2, 0, "final", 0))
|
||||
nested_scopes = _Feature((2, 1, 0, "beta", 1), (2, 2, 0, "final", 0))
|
||||
|
||||
This means that
|
||||
This means that::
|
||||
|
||||
from __future__ import nested_scopes
|
||||
from __future__ import nested_scopes
|
||||
|
||||
will work in all releases at or after 2.1b1, and that nested_scopes are
|
||||
intended to be enforced starting in release 2.2.
|
||||
will work in all releases at or after 2.1b1, and that nested_scopes are
|
||||
intended to be enforced starting in release 2.2.
|
||||
|
||||
|
||||
Resolved Problem: Runtime Compilation
|
||||
======================================
|
||||
|
||||
Several Python features can compile code during a module's runtime:
|
||||
Several Python features can compile code during a module's runtime:
|
||||
|
||||
1. The exec statement.
|
||||
2. The execfile() function.
|
||||
3. The compile() function.
|
||||
4. The eval() function.
|
||||
5. The input() function.
|
||||
1. The ``exec`` statement.
|
||||
2. The ``execfile()`` function.
|
||||
3. The ``compile()`` function.
|
||||
4. The ``eval()`` function.
|
||||
5. The ``input()`` function.
|
||||
|
||||
Since a module M containing a future_statement naming feature F
|
||||
explicitly requests that the current release act like a future release
|
||||
with respect to F, any code compiled dynamically from text passed to
|
||||
one of these from within M should probably also use the new syntax or
|
||||
semantics associated with F. The 2.1 release does behave this way.
|
||||
Since a module M containing a future_statement naming feature F explicitly
|
||||
requests that the current release act like a future release with respect to F,
|
||||
any code compiled dynamically from text passed to one of these from within M
|
||||
should probably also use the new syntax or semantics associated with F. The
|
||||
2.1 release does behave this way.
|
||||
|
||||
This isn't always desired, though. For example, doctest.testmod(M)
|
||||
compiles examples taken from strings in M, and those examples should
|
||||
use M's choices, not necessarily the doctest module's choices. In the
|
||||
2.1 release, this isn't possible, and no scheme has yet been suggested
|
||||
for working around this. NOTE: PEP 264 later addressed this in a
|
||||
flexible way, by adding optional arguments to compile().
|
||||
This isn't always desired, though. For example, ``doctest.testmod(M)``
|
||||
compiles examples taken from strings in M, and those examples should use M's
|
||||
choices, not necessarily the doctest module's choices. In the 2.1 release,
|
||||
this isn't possible, and no scheme has yet been suggested for working around
|
||||
this. NOTE: PEP 264 later addressed this in a flexible way, by adding
|
||||
optional arguments to ``compile()``.
|
||||
|
||||
In any case, a future_statement appearing "near the top" (see Syntax
|
||||
above) of text compiled dynamically by an exec, execfile() or compile()
|
||||
applies to the code block generated, but has no further effect on the
|
||||
module that executes such an exec, execfile() or compile(). This
|
||||
can't be used to affect eval() or input(), however, because they only
|
||||
allow expression input, and a future_statement is not an expression.
|
||||
In any case, a future_statement appearing "near the top" (see Syntax above) of
|
||||
text compiled dynamically by an ``exec``, ``execfile()`` or ``compile()``
|
||||
applies to the code block generated, but has no further effect on the module
|
||||
that executes such an ``exec``, ``execfile()`` or ``compile()``. This can't
|
||||
be used to affect ``eval()`` or ``input()``, however, because they only allow
|
||||
expression input, and a future_statement is not an expression.
|
||||
|
||||
|
||||
Resolved Problem: Native Interactive Shells
|
||||
============================================
|
||||
|
||||
There are two ways to get an interactive shell:
|
||||
There are two ways to get an interactive shell:
|
||||
|
||||
1. By invoking Python from a command line without a script argument.
|
||||
1. By invoking Python from a command line without a script argument.
|
||||
|
||||
2. By invoking Python from a command line with the -i switch and with a
|
||||
script argument.
|
||||
2. By invoking Python from a command line with the ``-i`` switch and with a
|
||||
script argument.
|
||||
|
||||
An interactive shell can be seen as an extreme case of runtime
|
||||
compilation (see above): in effect, each statement typed at an
|
||||
interactive shell prompt runs a new instance of exec, compile() or
|
||||
execfile(). A future_statement typed at an interactive shell applies to
|
||||
the rest of the shell session's life, as if the future_statement had
|
||||
appeared at the top of a module.
|
||||
An interactive shell can be seen as an extreme case of runtime compilation
|
||||
(see above): in effect, each statement typed at an interactive shell prompt
|
||||
runs a new instance of ``exec``, ``compile()`` or ``execfile()``. A
|
||||
future_statement typed at an interactive shell applies to the rest of the
|
||||
shell session's life, as if the future_statement had appeared at the top of a
|
||||
module.
|
||||
|
||||
|
||||
Resolved Problem: Simulated Interactive Shells
|
||||
===============================================
|
||||
|
||||
Interactive shells "built by hand" (by tools such as IDLE and the Emacs
|
||||
Python-mode) should behave like native interactive shells (see above).
|
||||
However, the machinery used internally by native interactive shells has
|
||||
not been exposed, and there isn't a clear way for tools building their
|
||||
own interactive shells to achieve the desired behavior.
|
||||
Interactive shells "built by hand" (by tools such as IDLE and the Emacs
|
||||
Python-mode) should behave like native interactive shells (see above).
|
||||
However, the machinery used internally by native interactive shells has not
|
||||
been exposed, and there isn't a clear way for tools building their own
|
||||
interactive shells to achieve the desired behavior.
|
||||
|
||||
NOTE: PEP 264 later addressed this, by adding intelligence to the
|
||||
standard codeop.py. Simulated shells that don't use the standard
|
||||
library shell helpers can get a similar effect by exploiting the
|
||||
new optional arguments to compile() added by PEP 264.
|
||||
NOTE: PEP 264 later addressed this, by adding intelligence to the standard
|
||||
``codeop.py``. Simulated shells that don't use the standard library shell
|
||||
helpers can get a similar effect by exploiting the new optional arguments to
|
||||
``compile()`` added by PEP 264.
|
||||
|
||||
|
||||
Questions and Answers
|
||||
=====================
|
||||
|
||||
Q: What about a "from __past__" version, to get back *old* behavior?
|
||||
What about a "from __past__" version, to get back *old* behavior?
|
||||
-----------------------------------------------------------------
|
||||
|
||||
A: Outside the scope of this PEP. Seems unlikely to the author,
|
||||
though. Write a PEP if you want to pursue it.
|
||||
Outside the scope of this PEP. Seems unlikely to the author, though. Write a
|
||||
PEP if you want to pursue it.
|
||||
|
||||
Q: What about incompatibilities due to changes in the Python virtual
|
||||
machine?
|
||||
What about incompatibilities due to changes in the Python virtual machine?
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
A: Outside the scope of this PEP, although PEP 5 [1] suggests a grace
|
||||
period there too, and the future_statement may also have a role to
|
||||
play there.
|
||||
Outside the scope of this PEP, although PEP 5 [1]_ suggests a grace period
|
||||
there too, and the future_statement may also have a role to play there.
|
||||
|
||||
Q: What about incompatibilities due to changes in Python's C API?
|
||||
What about incompatibilities due to changes in Python's C API?
|
||||
--------------------------------------------------------------
|
||||
|
||||
A: Outside the scope of this PEP.
|
||||
Outside the scope of this PEP.
|
||||
|
||||
Q: I want to wrap future_statements in try/except blocks, so I can
|
||||
use different code depending on which version of Python I'm running.
|
||||
Why can't I?
|
||||
I want to wrap future_statements in try/except blocks, so I can use different code depending on which version of Python I'm running. Why can't I?
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
A: Sorry! try/except is a runtime feature; future_statements are
|
||||
primarily compile-time gimmicks, and your try/except happens long
|
||||
after the compiler is done. That is, by the time you do
|
||||
try/except, the semantics in effect for the module are already a
|
||||
done deal. Since the try/except wouldn't accomplish what it
|
||||
*looks* like it should accomplish, it's simply not allowed. We
|
||||
also want to keep these special statements very easy to find and to
|
||||
recognize.
|
||||
Sorry! ``try/except`` is a runtime feature; future_statements are primarily
|
||||
compile-time gimmicks, and your ``try/except`` happens long after the compiler
|
||||
is done. That is, by the time you do ``try/except``, the semantics in effect
|
||||
for the module are already a done deal. Since the ``try/except`` wouldn't
|
||||
accomplish what it *looks* like it should accomplish, it's simply not allowed.
|
||||
We also want to keep these special statements very easy to find and to
|
||||
recognize.
|
||||
|
||||
Note that you *can* import __future__ directly, and use the
|
||||
information in it, along with sys.version_info, to figure out where
|
||||
the release you're running under stands in relation to a given
|
||||
feature's status.
|
||||
Note that you *can* import ``__future__`` directly, and use the information in
|
||||
it, along with ``sys.version_info``, to figure out where the release you're
|
||||
running under stands in relation to a given feature's status.
|
||||
|
||||
Q: Going back to the nested_scopes example, what if release 2.2 comes
|
||||
along and I still haven't changed my code? How can I keep the 2.1
|
||||
behavior then?
|
||||
Going back to the nested_scopes example, what if release 2.2 comes along and I still haven't changed my code? How can I keep the 2.1 behavior then?
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
A: By continuing to use 2.1, and not moving to 2.2 until you do change
|
||||
your code. The purpose of future_statement is to make life easier
|
||||
for people who keep current with the latest release in a timely
|
||||
fashion. We don't hate you if you don't, but your problems are
|
||||
much harder to solve, and somebody with those problems will need to
|
||||
write a PEP addressing them. future_statement is aimed at a
|
||||
different audience.
|
||||
By continuing to use 2.1, and not moving to 2.2 until you do change your
|
||||
code. The purpose of future_statement is to make life easier for people who
|
||||
keep current with the latest release in a timely fashion. We don't hate you
|
||||
if you don't, but your problems are much harder to solve, and somebody with
|
||||
those problems will need to write a PEP addressing them. future_statement is
|
||||
aimed at a different audience.
|
||||
|
||||
Q: Overloading "import" sucks. Why not introduce a new statement for
|
||||
this?
|
||||
Overloading ``import`` sucks. Why not introduce a new statement for this?
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
A: Like maybe "lambda lambda nested_scopes"? That is, unless we
|
||||
introduce a new keyword, we can't introduce an entirely new
|
||||
statement. But if we introduce a new keyword, that in itself
|
||||
would break old code. That would be too ironic to bear. Yes,
|
||||
overloading "import" does suck, but not as energetically as the
|
||||
alternatives -- as is, future_statements are 100% backward
|
||||
compatible.
|
||||
Like maybe ``lambda lambda nested_scopes``? That is, unless we introduce a
|
||||
new keyword, we can't introduce an entirely new statement. But if we
|
||||
introduce a new keyword, that in itself would break old code. That would be
|
||||
too ironic to bear. Yes, overloading ``import`` does suck, but not as
|
||||
energetically as the alternatives -- as is, future_statements are 100%
|
||||
backward compatible.
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document has been placed in the public domain.
|
||||
This document has been placed in the public domain.
|
||||
|
||||
|
||||
References and Footnotes
|
||||
========================
|
||||
|
||||
[1] PEP 5, Guidelines for Language Evolution, Prescod
|
||||
http://www.python.org/dev/peps/pep-0005/
|
||||
.. [1] PEP 5, Guidelines for Language Evolution, Prescod
|
||||
http://www.python.org/dev/peps/pep-0005/
|
||||
|
||||
[2] PEP 227, Statically Nested Scopes, Hylton
|
||||
http://www.python.org/dev/peps/pep-0227/
|
||||
.. [2] PEP 227, Statically Nested Scopes, Hylton
|
||||
http://www.python.org/dev/peps/pep-0227/
|
||||
|
||||
[3] PEP 230, Warning Framework, Van Rossum
|
||||
http://www.python.org/dev/peps/pep-0230/
|
||||
.. [3] PEP 230, Warning Framework, Van Rossum
|
||||
http://www.python.org/dev/peps/pep-0230/
|
||||
|
||||
[4] Note that this is "may" and not "will": better safe than sorry. Of
|
||||
course spurious warnings won't be generated when avoidable with
|
||||
reasonable cost.
|
||||
.. [4] Note that this is *may* and not *will*: better safe than sorry. Of course
|
||||
spurious warnings won't be generated when avoidable with reasonable cost.
|
||||
|
||||
[5] This ensures that a future_statement run under a release prior to
|
||||
the first one in which a given feature is known (but >= 2.1) will
|
||||
raise a compile-time error rather than silently do a wrong thing.
|
||||
If transported to a release prior to 2.1, a runtime error will be
|
||||
raised because of the failure to import __future__ (no such module
|
||||
existed in the standard distribution before the 2.1 release, and
|
||||
the double underscores make it a reserved name).
|
||||
.. [5] This ensures that a future_statement run under a release prior to the
|
||||
first one in which a given feature is known (but >= 2.1) will raise a
|
||||
compile-time error rather than silently do a wrong thing. If transported
|
||||
to a release prior to 2.1, a runtime error will be raised because of the
|
||||
failure to import ``__future__`` (no such module existed in the standard
|
||||
distribution before the 2.1 release, and the double underscores make it a
|
||||
reserved name).
|
||||
|
||||
|
||||
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
End:
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
End:
|
||||
|
|
226
pep-0247.txt
226
pep-0247.txt
|
@ -5,169 +5,169 @@ Last-Modified: $Date$
|
|||
Author: A.M. Kuchling <amk@amk.ca>
|
||||
Status: Final
|
||||
Type: Informational
|
||||
Content-Type: text/x-rst
|
||||
Created: 23-Mar-2001
|
||||
Post-History: 20-Sep-2001
|
||||
|
||||
Abstract
|
||||
|
||||
There are several different modules available that implement
|
||||
cryptographic hashing algorithms such as MD5 or SHA. This
|
||||
document specifies a standard API for such algorithms, to make it
|
||||
easier to switch between different implementations.
|
||||
Abstract
|
||||
========
|
||||
|
||||
There are several different modules available that implement cryptographic
|
||||
hashing algorithms such as MD5 or SHA. This document specifies a standard API
|
||||
for such algorithms, to make it easier to switch between different
|
||||
implementations.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
All hashing modules should present the same interface. Additional
|
||||
methods or variables can be added, but those described in this
|
||||
document should always be present.
|
||||
All hashing modules should present the same interface. Additional methods or
|
||||
variables can be added, but those described in this document should always be
|
||||
present.
|
||||
|
||||
Hash function modules define one function:
|
||||
Hash function modules define one function:
|
||||
|
||||
new([string]) (unkeyed hashes)
|
||||
new([key] , [string]) (keyed hashes)
|
||||
| ``new([string]) (unkeyed hashes)``
|
||||
| ``new([key] , [string]) (keyed hashes)``
|
||||
|
||||
Create a new hashing object and return it. The first form is
|
||||
for hashes that are unkeyed, such as MD5 or SHA. For keyed
|
||||
hashes such as HMAC, 'key' is a required parameter containing
|
||||
a string giving the key to use. In both cases, the optional
|
||||
'string' parameter, if supplied, will be immediately hashed
|
||||
into the object's starting state, as if obj.update(string) was
|
||||
called.
|
||||
Create a new hashing object and return it. The first form is for hashes
|
||||
that are unkeyed, such as MD5 or SHA. For keyed hashes such as HMAC, *key*
|
||||
is a required parameter containing a string giving the key to use. In both
|
||||
cases, the optional *string* parameter, if supplied, will be immediately
|
||||
hashed into the object's starting state, as if ``obj.update(string)``
|
||||
was called.
|
||||
|
||||
After creating a hashing object, arbitrary strings can be fed
|
||||
into the object using its update() method, and the hash value
|
||||
can be obtained at any time by calling the object's digest()
|
||||
method.
|
||||
After creating a hashing object, arbitrary strings can be fed into the
|
||||
object using its ``update()`` method, and the hash value can be obtained at
|
||||
any time by calling the object's ``digest()`` method.
|
||||
|
||||
Arbitrary additional keyword arguments can be added to this
|
||||
function, but if they're not supplied, sensible default values
|
||||
should be used. For example, 'rounds' and 'digest_size'
|
||||
keywords could be added for a hash function which supports a
|
||||
variable number of rounds and several different output sizes,
|
||||
and they should default to values believed to be secure.
|
||||
Arbitrary additional keyword arguments can be added to this function, but if
|
||||
they're not supplied, sensible default values should be used. For example,
|
||||
``rounds`` and ``digest_size`` keywords could be added for a hash function
|
||||
which supports a variable number of rounds and several different output
|
||||
sizes, and they should default to values believed to be secure.
|
||||
|
||||
Hash function modules define one variable:
|
||||
Hash function modules define one variable:
|
||||
|
||||
digest_size
|
||||
| ``digest_size``
|
||||
|
||||
An integer value; the size of the digest produced by the
|
||||
hashing objects created by this module, measured in bytes.
|
||||
You could also obtain this value by creating a sample object
|
||||
and accessing its 'digest_size' attribute, but it can be
|
||||
convenient to have this value available from the module.
|
||||
Hashes with a variable output size will set this variable to
|
||||
None.
|
||||
An integer value; the size of the digest produced by the hashing objects
|
||||
created by this module, measured in bytes. You could also obtain this value
|
||||
by creating a sample object and accessing its ``digest_size`` attribute, but
|
||||
it can be convenient to have this value available from the module. Hashes
|
||||
with a variable output size will set this variable to ``None``.
|
||||
|
||||
Hashing objects require a single attribute:
|
||||
Hashing objects require a single attribute:
|
||||
|
||||
digest_size
|
||||
| ``digest_size``
|
||||
|
||||
This attribute is identical to the module-level digest_size
|
||||
variable, measuring the size of the digest produced by the
|
||||
hashing object, measured in bytes. If the hash has a variable
|
||||
output size, this output size must be chosen when the hashing
|
||||
object is created, and this attribute must contain the
|
||||
selected size. Therefore, None is *not* a legal value for this
|
||||
attribute.
|
||||
This attribute is identical to the module-level ``digest_size`` variable,
|
||||
measuring the size of the digest produced by the hashing object, measured in
|
||||
bytes. If the hash has a variable output size, this output size must be
|
||||
chosen when the hashing object is created, and this attribute must contain
|
||||
the selected size. Therefore, ``None`` is *not* a legal value for this
|
||||
attribute.
|
||||
|
||||
|
||||
Hashing objects require the following methods:
|
||||
Hashing objects require the following methods:
|
||||
|
||||
copy()
|
||||
| ``copy()``
|
||||
|
||||
Return a separate copy of this hashing object. An update to
|
||||
this copy won't affect the original object.
|
||||
Return a separate copy of this hashing object. An update to this copy won't
|
||||
affect the original object.
|
||||
|
||||
digest()
|
||||
| ``digest()``
|
||||
|
||||
Return the hash value of this hashing object as a string
|
||||
containing 8-bit data. The object is not altered in any way
|
||||
by this function; you can continue updating the object after
|
||||
calling this function.
|
||||
Return the hash value of this hashing object as a string containing 8-bit
|
||||
data. The object is not altered in any way by this function; you can
|
||||
continue updating the object after calling this function.
|
||||
|
||||
hexdigest()
|
||||
| ``hexdigest()``
|
||||
|
||||
Return the hash value of this hashing object as a string
|
||||
containing hexadecimal digits. Lowercase letters should be used
|
||||
for the digits 'a' through 'f'. Like the .digest() method, this
|
||||
method mustn't alter the object.
|
||||
Return the hash value of this hashing object as a string containing
|
||||
hexadecimal digits. Lowercase letters should be used for the digits ``a``
|
||||
through ``f``. Like the ``.digest()`` method, this method mustn't alter the
|
||||
object.
|
||||
|
||||
update(string)
|
||||
| ``update(string)``
|
||||
|
||||
Hash 'string' into the current state of the hashing object.
|
||||
update() can be called any number of times during a hashing
|
||||
object's lifetime.
|
||||
Hash *string* into the current state of the hashing object. ``update()`` can
|
||||
be called any number of times during a hashing object's lifetime.
|
||||
|
||||
Hashing modules can define additional module-level functions or
|
||||
object methods and still be compliant with this specification.
|
||||
Hashing modules can define additional module-level functions or object methods
|
||||
and still be compliant with this specification.
|
||||
|
||||
Here's an example, using a module named 'MD5':
|
||||
Here's an example, using a module named ``MD5``::
|
||||
|
||||
>>> from Crypto.Hash import MD5
|
||||
>>> m = MD5.new()
|
||||
>>> m.digest_size
|
||||
16
|
||||
>>> m.update('abc')
|
||||
>>> m.digest()
|
||||
'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr'
|
||||
>>> m.hexdigest()
|
||||
'900150983cd24fb0d6963f7d28e17f72'
|
||||
>>> MD5.new('abc').digest()
|
||||
'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr'
|
||||
>>> from Crypto.Hash import MD5
|
||||
>>> m = MD5.new()
|
||||
>>> m.digest_size
|
||||
16
|
||||
>>> m.update('abc')
|
||||
>>> m.digest()
|
||||
'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr'
|
||||
>>> m.hexdigest()
|
||||
'900150983cd24fb0d6963f7d28e17f72'
|
||||
>>> MD5.new('abc').digest()
|
||||
'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr'
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
||||
The digest size is measured in bytes, not bits, even though hash
|
||||
algorithm sizes are usually quoted in bits; MD5 is a 128-bit
|
||||
algorithm and not a 16-byte one, for example. This is because, in
|
||||
the sample code I looked at, the length in bytes is often needed
|
||||
(to seek ahead or behind in a file; to compute the length of an
|
||||
output string) while the length in bits is rarely used.
|
||||
Therefore, the burden will fall on the few people actually needing
|
||||
the size in bits, who will have to multiply digest_size by 8.
|
||||
The digest size is measured in bytes, not bits, even though hash algorithm
|
||||
sizes are usually quoted in bits; MD5 is a 128-bit algorithm and not a 16-byte
|
||||
one, for example. This is because, in the sample code I looked at, the length
|
||||
in bytes is often needed (to seek ahead or behind in a file; to compute the
|
||||
length of an output string) while the length in bits is rarely used. Therefore,
|
||||
the burden will fall on the few people actually needing the size in bits, who
|
||||
will have to multiply ``digest_size`` by 8.
|
||||
|
||||
It's been suggested that the update() method would be better named
|
||||
append(). However, that method is really causing the current
|
||||
state of the hashing object to be updated, and update() is already
|
||||
used by the md5 and sha modules included with Python, so it seems
|
||||
simplest to leave the name update() alone.
|
||||
It's been suggested that the ``update()`` method would be better named
|
||||
``append()``. However, that method is really causing the current state of the
|
||||
hashing object to be updated, and ``update()`` is already used by the md5 and
|
||||
sha modules included with Python, so it seems simplest to leave the name
|
||||
``update()`` alone.
|
||||
|
||||
The order of the constructor's arguments for keyed hashes was a
|
||||
sticky issue. It wasn't clear whether the key should come first
|
||||
or second. It's a required parameter, and the usual convention is
|
||||
to place required parameters first, but that also means that the
|
||||
'string' parameter moves from the first position to the second.
|
||||
It would be possible to get confused and pass a single argument to
|
||||
a keyed hash, thinking that you're passing an initial string to an
|
||||
unkeyed hash, but it doesn't seem worth making the interface
|
||||
for keyed hashes more obscure to avoid this potential error.
|
||||
The order of the constructor's arguments for keyed hashes was a sticky issue.
|
||||
It wasn't clear whether the *key* should come first or second. It's a required
|
||||
parameter, and the usual convention is to place required parameters first, but
|
||||
that also means that the *string* parameter moves from the first position to
|
||||
the second. It would be possible to get confused and pass a single argument to
|
||||
a keyed hash, thinking that you're passing an initial string to an unkeyed
|
||||
hash, but it doesn't seem worth making the interface for keyed hashes more
|
||||
obscure to avoid this potential error.
|
||||
|
||||
|
||||
Changes
|
||||
=======
|
||||
|
||||
2001-09-17: Renamed clear() to reset(); added digest_size attribute
|
||||
to objects; added .hexdigest() method.
|
||||
2001-09-20: Removed reset() method completely.
|
||||
2001-09-28: Set digest_size to None for variable-size hashes.
|
||||
2001-09-17: Renamed ``clear()`` to ``reset()``; added ``digest_size`` attribute
|
||||
to objects; added ``.hexdigest()`` method.
|
||||
|
||||
2001-09-20: Removed ``reset()`` method completely.
|
||||
|
||||
2001-09-28: Set ``digest_size`` to ``None`` for variable-size hashes.
|
||||
|
||||
|
||||
Acknowledgements
|
||||
================
|
||||
|
||||
Thanks to Aahz, Andrew Archibald, Rich Salz, Itamar
|
||||
Shtull-Trauring, and the readers of the python-crypto list for
|
||||
their comments on this PEP.
|
||||
Thanks to Aahz, Andrew Archibald, Rich Salz, Itamar Shtull-Trauring, and the
|
||||
readers of the python-crypto list for their comments on this PEP.
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document has been placed in the public domain.
|
||||
This document has been placed in the public domain.
|
||||
|
||||
|
||||
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
End:
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
End:
|
||||
|
||||
|
|
945
pep-0342.txt
945
pep-0342.txt
File diff suppressed because it is too large
Load Diff
747
pep-0344.txt
747
pep-0344.txt
|
@ -5,541 +5,560 @@ Last-Modified: $Date$
|
|||
Author: Ka-Ping Yee
|
||||
Status: Superseded
|
||||
Type: Standards Track
|
||||
Content-Type: text/plain
|
||||
Content-Type: text/x-rst
|
||||
Created: 12-May-2005
|
||||
Python-Version: 2.5
|
||||
Post-History:
|
||||
|
||||
|
||||
Numbering Note
|
||||
==============
|
||||
|
||||
This PEP has been renumbered to PEP 3134. The text below is the
|
||||
last version submitted under the old number.
|
||||
This PEP has been renumbered to PEP 3134. The text below is the last version
|
||||
submitted under the old number.
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
This PEP proposes three standard attributes on exception instances:
|
||||
the '__context__' attribute for implicitly chained exceptions, the
|
||||
'__cause__' attribute for explicitly chained exceptions, and the
|
||||
'__traceback__' attribute for the traceback. A new "raise ... from"
|
||||
statement sets the '__cause__' attribute.
|
||||
This PEP proposes three standard attributes on exception instances: the
|
||||
``__context__`` attribute for implicitly chained exceptions, the
|
||||
``__cause__`` attribute for explicitly chained exceptions, and the
|
||||
``__traceback__`` attribute for the traceback. A new ``raise ... from``
|
||||
statement sets the ``__cause__`` attribute.
|
||||
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
During the handling of one exception (exception A), it is possible
|
||||
that another exception (exception B) may occur. In today's Python
|
||||
(version 2.4), if this happens, exception B is propagated outward
|
||||
and exception A is lost. In order to debug the problem, it is
|
||||
useful to know about both exceptions. The '__context__' attribute
|
||||
retains this information automatically.
|
||||
During the handling of one exception (exception A), it is possible that another
|
||||
exception (exception B) may occur. In today's Python (version 2.4), if this
|
||||
happens, exception B is propagated outward and exception A is lost. In order
|
||||
to debug the problem, it is useful to know about both exceptions. The
|
||||
``__context__`` attribute retains this information automatically.
|
||||
|
||||
Sometimes it can be useful for an exception handler to intentionally
|
||||
re-raise an exception, either to provide extra information or to
|
||||
translate an exception to another type. The '__cause__' attribute
|
||||
provides an explicit way to record the direct cause of an exception.
|
||||
Sometimes it can be useful for an exception handler to intentionally re-raise
|
||||
an exception, either to provide extra information or to translate an exception
|
||||
to another type. The ``__cause__`` attribute provides an explicit way to
|
||||
record the direct cause of an exception.
|
||||
|
||||
In today's Python implementation, exceptions are composed of three
|
||||
parts: the type, the value, and the traceback. The 'sys' module,
|
||||
exposes the current exception in three parallel variables, exc_type,
|
||||
exc_value, and exc_traceback, the sys.exc_info() function returns a
|
||||
tuple of these three parts, and the 'raise' statement has a
|
||||
three-argument form accepting these three parts. Manipulating
|
||||
exceptions often requires passing these three things in parallel,
|
||||
which can be tedious and error-prone. Additionally, the 'except'
|
||||
statement can only provide access to the value, not the traceback.
|
||||
Adding the '__traceback__' attribute to exception values makes all
|
||||
the exception information accessible from a single place.
|
||||
In today's Python implementation, exceptions are composed of three parts: the
|
||||
type, the value, and the traceback. The ``sys`` module, exposes the current
|
||||
exception in three parallel variables, ``exc_type``, ``exc_value``, and
|
||||
``exc_traceback``, the ``sys.exc_info()`` function returns a tuple of these
|
||||
three parts, and the ``raise`` statement has a three-argument form accepting
|
||||
these three parts. Manipulating exceptions often requires passing these three
|
||||
things in parallel, which can be tedious and error-prone. Additionally, the
|
||||
``except`` statement can only provide access to the value, not the traceback.
|
||||
Adding the ``__traceback__`` attribute to exception values makes all the
|
||||
exception information accessible from a single place.
|
||||
|
||||
|
||||
History
|
||||
=======
|
||||
|
||||
Raymond Hettinger [1] raised the issue of masked exceptions on
|
||||
Python-Dev in January 2003 and proposed a PyErr_FormatAppend()
|
||||
function that C modules could use to augment the currently active
|
||||
exception with more information. Brett Cannon [2] brought up
|
||||
chained exceptions again in June 2003, prompting a long discussion.
|
||||
Raymond Hettinger [1]_ raised the issue of masked exceptions on Python-Dev in
|
||||
January 2003 and proposed a ``PyErr_FormatAppend()`` function that C modules
|
||||
could use to augment the currently active exception with more information.
|
||||
Brett Cannon [2]_ brought up chained exceptions again in June 2003, prompting
|
||||
a long discussion.
|
||||
|
||||
Greg Ewing [3] identified the case of an exception occurring in a
|
||||
'finally' block during unwinding triggered by an original exception,
|
||||
as distinct from the case of an exception occurring in an 'except'
|
||||
block that is handling the original exception.
|
||||
Greg Ewing [3]_ identified the case of an exception occurring in a ``finally``
|
||||
block during unwinding triggered by an original exception, as distinct from
|
||||
the case of an exception occurring in an ``except`` block that is handling the
|
||||
original exception.
|
||||
|
||||
Greg Ewing [4] and Guido van Rossum [5], and probably others, have
|
||||
previously mentioned adding a traceback attribute to Exception
|
||||
instances. This is noted in PEP 3000.
|
||||
Greg Ewing [4]_ and Guido van Rossum [5]_, and probably others, have
|
||||
previously mentioned adding a traceback attribute to ``Exception`` instances.
|
||||
This is noted in PEP 3000.
|
||||
|
||||
This PEP was motivated by yet another recent Python-Dev reposting
|
||||
of the same ideas [6] [7].
|
||||
This PEP was motivated by yet another recent Python-Dev reposting of the same
|
||||
ideas [6]_ [7]_.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
||||
The Python-Dev discussions revealed interest in exception chaining
|
||||
for two quite different purposes. To handle the unexpected raising
|
||||
of a secondary exception, the exception must be retained implicitly.
|
||||
To support intentional translation of an exception, there must be a
|
||||
way to chain exceptions explicitly. This PEP addresses both.
|
||||
The Python-Dev discussions revealed interest in exception chaining for two
|
||||
quite different purposes. To handle the unexpected raising of a secondary
|
||||
exception, the exception must be retained implicitly. To support intentional
|
||||
translation of an exception, there must be a way to chain exceptions
|
||||
explicitly. This PEP addresses both.
|
||||
|
||||
Several attribute names for chained exceptions have been suggested
|
||||
on Python-Dev [2], including 'cause', 'antecedent', 'reason',
|
||||
'original', 'chain', 'chainedexc', 'exc_chain', 'excprev',
|
||||
'previous', and 'precursor'. For an explicitly chained exception,
|
||||
this PEP suggests '__cause__' because of its specific meaning. For
|
||||
an implicitly chained exception, this PEP proposes the name
|
||||
'__context__' because the intended meaning is more specific than
|
||||
temporal precedence but less specific than causation: an exception
|
||||
occurs in the context of handling another exception.
|
||||
Several attribute names for chained exceptions have been suggested on Python-
|
||||
Dev [2]_, including ``cause``, ``antecedent``, ``reason``, ``original``,
|
||||
``chain``, ``chainedexc``, ``xc_chain``, ``excprev``, ``previous`` and
|
||||
``precursor``. For an explicitly chained exception, this PEP suggests
|
||||
``__cause__`` because of its specific meaning. For an implicitly chained
|
||||
exception, this PEP proposes the name ``__context__`` because the intended
|
||||
meaning is more specific than temporal precedence but less specific than
|
||||
causation: an exception occurs in the context of handling another exception.
|
||||
|
||||
This PEP suggests names with leading and trailing double-underscores
|
||||
for these three attributes because they are set by the Python VM.
|
||||
Only in very special cases should they be set by normal assignment.
|
||||
This PEP suggests names with leading and trailing double-underscores for these
|
||||
three attributes because they are set by the Python VM. Only in very special
|
||||
cases should they be set by normal assignment.
|
||||
|
||||
This PEP handles exceptions that occur during 'except' blocks and
|
||||
'finally' blocks in the same way. Reading the traceback makes it
|
||||
clear where the exceptions occurred, so additional mechanisms for
|
||||
distinguishing the two cases would only add unnecessary complexity.
|
||||
This PEP handles exceptions that occur during ``except`` blocks and
|
||||
``finally`` blocks in the same way. Reading the traceback makes it clear
|
||||
where the exceptions occurred, so additional mechanisms for distinguishing
|
||||
the two cases would only add unnecessary complexity.
|
||||
|
||||
This PEP proposes that the outermost exception object (the one
|
||||
exposed for matching by 'except' clauses) be the most recently
|
||||
raised exception for compatibility with current behaviour.
|
||||
This PEP proposes that the outermost exception object (the one exposed for
|
||||
matching by ``except`` clauses) be the most recently raised exception for
|
||||
compatibility with current behaviour.
|
||||
|
||||
This PEP proposes that tracebacks display the outermost exception
|
||||
last, because this would be consistent with the chronological order
|
||||
of tracebacks (from oldest to most recent frame) and because the
|
||||
actual thrown exception is easier to find on the last line.
|
||||
This PEP proposes that tracebacks display the outermost exception last,
|
||||
because this would be consistent with the chronological order of tracebacks
|
||||
(from oldest to most recent frame) and because the actual thrown exception is
|
||||
easier to find on the last line.
|
||||
|
||||
To keep things simpler, the C API calls for setting an exception
|
||||
will not automatically set the exception's '__context__'. Guido
|
||||
van Rossum has expressed concerns with making such changes [8].
|
||||
To keep things simpler, the C API calls for setting an exception will not
|
||||
automatically set the exception's ``__context__``. Guido van Rossum has
|
||||
expressed concerns with making such changes [8]_.
|
||||
|
||||
As for other languages, Java and Ruby both discard the original
|
||||
exception when another exception occurs in a 'catch'/'rescue' or
|
||||
'finally'/'ensure' clause. Perl 5 lacks built-in structured
|
||||
exception handling. For Perl 6, RFC number 88 [9] proposes an exception
|
||||
mechanism that implicitly retains chained exceptions in an array
|
||||
named @@. In that RFC, the most recently raised exception is
|
||||
exposed for matching, as in this PEP; also, arbitrary expressions
|
||||
(possibly involving @@) can be evaluated for exception matching.
|
||||
As for other languages, Java and Ruby both discard the original exception when
|
||||
another exception occurs in a ``catch/rescue`` or ``finally/ensure`` clause.
|
||||
Perl 5 lacks built-in structured exception handling. For Perl 6, RFC number
|
||||
88 [9]_ proposes an exception mechanism that implicitly retains chained
|
||||
exceptions in an array named ``@@``. In that RFC, the most recently raised
|
||||
exception is exposed for matching, as in this PEP; also, arbitrary expressions
|
||||
(possibly involving ``@@``) can be evaluated for exception matching.
|
||||
|
||||
Exceptions in C# contain a read-only 'InnerException' property that
|
||||
may point to another exception. Its documentation [10] says that
|
||||
"When an exception X is thrown as a direct result of a previous
|
||||
exception Y, the InnerException property of X should contain a
|
||||
reference to Y." This property is not set by the VM automatically;
|
||||
rather, all exception constructors take an optional 'innerException'
|
||||
argument to set it explicitly. The '__cause__' attribute fulfills
|
||||
the same purpose as InnerException, but this PEP proposes a new form
|
||||
of 'raise' rather than extending the constructors of all exceptions.
|
||||
C# also provides a GetBaseException method that jumps directly to
|
||||
the end of the InnerException chain; this PEP proposes no analog.
|
||||
Exceptions in C# contain a read-only ``InnerException`` property that may
|
||||
point to another exception. Its documentation [10]_ says that "When an
|
||||
exception X is thrown as a direct result of a previous exception Y, the
|
||||
``InnerException`` property of X should contain a reference to Y." This
|
||||
property is not set by the VM automatically; rather, all exception
|
||||
constructors take an optional ``innerException`` argument to set it
|
||||
explicitly. The ``__cause__`` attribute fulfills the same purpose as
|
||||
``InnerException``, but this PEP proposes a new form of ``raise`` rather than
|
||||
extending the constructors of all exceptions. C# also provides a
|
||||
``GetBaseException`` method that jumps directly to the end of the
|
||||
``InnerException`` chain; this PEP proposes no analog.
|
||||
|
||||
The reason all three of these attributes are presented together in
|
||||
one proposal is that the '__traceback__' attribute provides
|
||||
convenient access to the traceback on chained exceptions.
|
||||
The reason all three of these attributes are presented together in one proposal
|
||||
is that the ``__traceback__`` attribute provides convenient access to the
|
||||
traceback on chained exceptions.
|
||||
|
||||
|
||||
Implicit Exception Chaining
|
||||
===========================
|
||||
|
||||
Here is an example to illustrate the '__context__' attribute.
|
||||
Here is an example to illustrate the ``__context__`` attribute::
|
||||
|
||||
def compute(a, b):
|
||||
def compute(a, b):
|
||||
try:
|
||||
a/b
|
||||
except Exception, exc:
|
||||
log(exc)
|
||||
|
||||
def log(exc):
|
||||
file = open('logfile.txt') # oops, forgot the 'w'
|
||||
print >>file, exc
|
||||
file.close()
|
||||
|
||||
Calling ``compute(0, 0)`` causes a ``ZeroDivisionError``. The ``compute()``
|
||||
function catches this exception and calls ``log(exc)``, but the ``log()``
|
||||
function also raises an exception when it tries to write to a file that wasn't
|
||||
opened for writing.
|
||||
|
||||
In today's Python, the caller of ``compute()`` gets thrown an ``IOError``. The
|
||||
``ZeroDivisionError`` is lost. With the proposed change, the instance of
|
||||
``IOError`` has an additional ``__context__`` attribute that retains the
|
||||
``ZeroDivisionError``.
|
||||
|
||||
The following more elaborate example demonstrates the handling of a mixture of
|
||||
``finally`` and ``except`` clauses::
|
||||
|
||||
def main(filename):
|
||||
file = open(filename) # oops, forgot the 'w'
|
||||
try:
|
||||
try:
|
||||
a/b
|
||||
compute()
|
||||
except Exception, exc:
|
||||
log(exc)
|
||||
|
||||
def log(exc):
|
||||
file = open('logfile.txt') # oops, forgot the 'w'
|
||||
print >>file, exc
|
||||
file.close()
|
||||
|
||||
Calling compute(0, 0) causes a ZeroDivisionError. The compute()
|
||||
function catches this exception and calls log(exc), but the log()
|
||||
function also raises an exception when it tries to write to a
|
||||
file that wasn't opened for writing.
|
||||
|
||||
In today's Python, the caller of compute() gets thrown an IOError.
|
||||
The ZeroDivisionError is lost. With the proposed change, the
|
||||
instance of IOError has an additional '__context__' attribute that
|
||||
retains the ZeroDivisionError.
|
||||
|
||||
The following more elaborate example demonstrates the handling of a
|
||||
mixture of 'finally' and 'except' clauses:
|
||||
|
||||
def main(filename):
|
||||
file = open(filename) # oops, forgot the 'w'
|
||||
try:
|
||||
try:
|
||||
compute()
|
||||
except Exception, exc:
|
||||
log(file, exc)
|
||||
log(file, exc)
|
||||
finally:
|
||||
file.clos() # oops, misspelled 'close'
|
||||
file.clos() # oops, misspelled 'close'
|
||||
|
||||
def compute():
|
||||
1/0
|
||||
def compute():
|
||||
1/0
|
||||
|
||||
def log(file, exc):
|
||||
try:
|
||||
print >>file, exc # oops, file is not writable
|
||||
except:
|
||||
display(exc)
|
||||
def log(file, exc):
|
||||
try:
|
||||
print >>file, exc # oops, file is not writable
|
||||
except:
|
||||
display(exc)
|
||||
|
||||
def display(exc):
|
||||
print ex # oops, misspelled 'exc'
|
||||
def display(exc):
|
||||
print ex # oops, misspelled 'exc'
|
||||
|
||||
Calling main() with the name of an existing file will trigger four
|
||||
exceptions. The ultimate result will be an AttributeError due to
|
||||
the misspelling of 'clos', whose __context__ points to a NameError
|
||||
due to the misspelling of 'ex', whose __context__ points to an
|
||||
IOError due to the file being read-only, whose __context__ points to
|
||||
a ZeroDivisionError, whose __context__ attribute is None.
|
||||
Calling ``main()`` with the name of an existing file will trigger four
|
||||
exceptions. The ultimate result will be an ``AttributeError`` due to the
|
||||
misspelling of ``clos``, whose ``__context__`` points to a ``NameError`` due
|
||||
to the misspelling of ``ex``, whose ``__context__`` points to an ``IOError``
|
||||
due to the file being read-only, whose ``__context__`` points to a
|
||||
``ZeroDivisionError``, whose ``__context__`` attribute is ``None``.
|
||||
|
||||
The proposed semantics are as follows:
|
||||
The proposed semantics are as follows:
|
||||
|
||||
1. Each thread has an exception context initially set to None.
|
||||
1. Each thread has an exception context initially set to ``None``.
|
||||
|
||||
2. Whenever an exception is raised, if the exception instance does
|
||||
not already have a '__context__' attribute, the interpreter sets
|
||||
it equal to the thread's exception context.
|
||||
2. Whenever an exception is raised, if the exception instance does not
|
||||
already have a ``__context__`` attribute, the interpreter sets it equal to
|
||||
the thread's exception context.
|
||||
|
||||
3. Immediately after an exception is raised, the thread's exception
|
||||
context is set to the exception.
|
||||
3. Immediately after an exception is raised, the thread's exception context is
|
||||
set to the exception.
|
||||
|
||||
4. Whenever the interpreter exits an 'except' block by reaching the
|
||||
end or executing a 'return', 'yield', 'continue', or 'break'
|
||||
statement, the thread's exception context is set to None.
|
||||
4. Whenever the interpreter exits an ``except`` block by reaching the end or
|
||||
executing a ``return``, ``yield``, ``continue``, or ``break`` statement,
|
||||
the thread's exception context is set to ``None``.
|
||||
|
||||
|
||||
Explicit Exception Chaining
|
||||
===========================
|
||||
|
||||
The '__cause__' attribute on exception objects is always initialized
|
||||
to None. It is set by a new form of the 'raise' statement:
|
||||
The ``__cause__`` attribute on exception objects is always initialized to
|
||||
``None``. It is set by a new form of the ``raise`` statement::
|
||||
|
||||
raise EXCEPTION from CAUSE
|
||||
raise EXCEPTION from CAUSE
|
||||
|
||||
which is equivalent to:
|
||||
which is equivalent to::
|
||||
|
||||
exc = EXCEPTION
|
||||
exc.__cause__ = CAUSE
|
||||
raise exc
|
||||
exc = EXCEPTION
|
||||
exc.__cause__ = CAUSE
|
||||
raise exc
|
||||
|
||||
In the following example, a database provides implementations for a
|
||||
few different kinds of storage, with file storage as one kind. The
|
||||
database designer wants errors to propagate as DatabaseError objects
|
||||
so that the client doesn't have to be aware of the storage-specific
|
||||
details, but doesn't want to lose the underlying error information.
|
||||
In the following example, a database provides implementations for a few
|
||||
different kinds of storage, with file storage as one kind. The database
|
||||
designer wants errors to propagate as ``DatabaseError`` objects so that the
|
||||
client doesn't have to be aware of the storage-specific details, but doesn't
|
||||
want to lose the underlying error information::
|
||||
|
||||
class DatabaseError(StandardError):
|
||||
pass
|
||||
class DatabaseError(StandardError):
|
||||
pass
|
||||
|
||||
class FileDatabase(Database):
|
||||
def __init__(self, filename):
|
||||
try:
|
||||
self.file = open(filename)
|
||||
except IOError, exc:
|
||||
raise DatabaseError('failed to open') from exc
|
||||
class FileDatabase(Database):
|
||||
def __init__(self, filename):
|
||||
try:
|
||||
self.file = open(filename)
|
||||
except IOError, exc:
|
||||
raise DatabaseError('failed to open') from exc
|
||||
|
||||
If the call to open() raises an exception, the problem will be
|
||||
reported as a DatabaseError, with a __cause__ attribute that reveals
|
||||
the IOError as the original cause.
|
||||
If the call to ``open()`` raises an exception, the problem will be reported as
|
||||
a ``DatabaseError``, with a ``__cause__`` attribute that reveals the
|
||||
``IOError`` as the original cause.
|
||||
|
||||
|
||||
Traceback Attribute
|
||||
===================
|
||||
|
||||
The following example illustrates the '__traceback__' attribute.
|
||||
The following example illustrates the ``__traceback__`` attribute::
|
||||
|
||||
def do_logged(file, work):
|
||||
try:
|
||||
work()
|
||||
except Exception, exc:
|
||||
write_exception(file, exc)
|
||||
raise exc
|
||||
def do_logged(file, work):
|
||||
try:
|
||||
work()
|
||||
except Exception, exc:
|
||||
write_exception(file, exc)
|
||||
raise exc
|
||||
|
||||
from traceback import format_tb
|
||||
from traceback import format_tb
|
||||
|
||||
def write_exception(file, exc):
|
||||
...
|
||||
type = exc.__class__
|
||||
message = str(exc)
|
||||
lines = format_tb(exc.__traceback__)
|
||||
file.write(... type ... message ... lines ...)
|
||||
...
|
||||
def write_exception(file, exc):
|
||||
...
|
||||
type = exc.__class__
|
||||
message = str(exc)
|
||||
lines = format_tb(exc.__traceback__)
|
||||
file.write(... type ... message ... lines ...)
|
||||
...
|
||||
|
||||
In today's Python, the do_logged() function would have to extract
|
||||
the traceback from sys.exc_traceback or sys.exc_info()[2] and pass
|
||||
both the value and the traceback to write_exception(). With the
|
||||
proposed change, write_exception() simply gets one argument and
|
||||
obtains the exception using the '__traceback__' attribute.
|
||||
In today's Python, the ``do_logged()`` function would have to extract the
|
||||
traceback from ``sys.exc_traceback`` or ``sys.exc_info()`` [2]_ and pass both
|
||||
the value and the traceback to ``write_exception()``. With the proposed
|
||||
change, ``write_exception()`` simply gets one argument and obtains the
|
||||
exception using the ``__traceback__`` attribute.
|
||||
|
||||
The proposed semantics are as follows:
|
||||
The proposed semantics are as follows:
|
||||
|
||||
1. Whenever an exception is caught, if the exception instance does
|
||||
not already have a '__traceback__' attribute, the interpreter
|
||||
sets it to the newly caught traceback.
|
||||
1. Whenever an exception is caught, if the exception instance does not already
|
||||
have a ``__traceback__`` attribute, the interpreter sets it to the newly
|
||||
caught traceback.
|
||||
|
||||
|
||||
Enhanced Reporting
|
||||
==================
|
||||
|
||||
The default exception handler will be modified to report chained
|
||||
exceptions. The chain of exceptions is traversed by following the
|
||||
'__cause__' and '__context__' attributes, with '__cause__' taking
|
||||
priority. In keeping with the chronological order of tracebacks,
|
||||
the most recently raised exception is displayed last; that is, the
|
||||
display begins with the description of the innermost exception and
|
||||
backs up the chain to the outermost exception. The tracebacks are
|
||||
formatted as usual, with one of the lines:
|
||||
The default exception handler will be modified to report chained exceptions.
|
||||
The chain of exceptions is traversed by following the ``__cause__`` and
|
||||
``__context__`` attributes, with ``__cause__`` taking priority. In keeping
|
||||
with the chronological order of tracebacks, the most recently raised exception
|
||||
is displayed last; that is, the display begins with the description of the
|
||||
innermost exception and backs up the chain to the outermost exception. The
|
||||
tracebacks are formatted as usual, with one of the lines::
|
||||
|
||||
The above exception was the direct cause of the following exception:
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
or
|
||||
or
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
::
|
||||
|
||||
between tracebacks, depending whether they are linked by __cause__
|
||||
or __context__ respectively. Here is a sketch of the procedure:
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
def print_chain(exc):
|
||||
if exc.__cause__:
|
||||
print_chain(exc.__cause__)
|
||||
print '\nThe above exception was the direct cause...'
|
||||
elif exc.__context__:
|
||||
print_chain(exc.__context__)
|
||||
print '\nDuring handling of the above exception, ...'
|
||||
print_exc(exc)
|
||||
between tracebacks, depending whether they are linked by ``__cause__`` or
|
||||
``__context__`` respectively. Here is a sketch of the procedure::
|
||||
|
||||
In the 'traceback' module, the format_exception, print_exception,
|
||||
print_exc, and print_last functions will be updated to accept an
|
||||
optional 'chain' argument, True by default. When this argument is
|
||||
True, these functions will format or display the entire chain of
|
||||
exceptions as just described. When it is False, these functions
|
||||
will format or display only the outermost exception.
|
||||
def print_chain(exc):
|
||||
if exc.__cause__:
|
||||
print_chain(exc.__cause__)
|
||||
print '\nThe above exception was the direct cause...'
|
||||
elif exc.__context__:
|
||||
print_chain(exc.__context__)
|
||||
print '\nDuring handling of the above exception, ...'
|
||||
print_exc(exc)
|
||||
|
||||
The 'cgitb' module should also be updated to display the entire
|
||||
chain of exceptions.
|
||||
In the ``traceback`` module, the ``format_exception``, ``print_exception``,
|
||||
``print_exc``, and ``print_last functions`` will be updated to accept an
|
||||
optional ``chain`` argument, ``True`` by default. When this argument is
|
||||
``True``, these functions will format or display the entire chain of
|
||||
exceptions as just described. When it is ``False``, these functions will
|
||||
format or display only the outermost exception.
|
||||
|
||||
The ``cgitb`` module should also be updated to display the entire chain of
|
||||
exceptions.
|
||||
|
||||
|
||||
C API
|
||||
=====
|
||||
|
||||
The PyErr_Set* calls for setting exceptions will not set the
|
||||
'__context__' attribute on exceptions. PyErr_NormalizeException
|
||||
will always set the 'traceback' attribute to its 'tb' argument and
|
||||
the '__context__' and '__cause__' attributes to None.
|
||||
The ``PyErr_Set*`` calls for setting exceptions will not set the
|
||||
``__context__`` attribute on exceptions. ``PyErr_NormalizeException`` will
|
||||
always set the ``traceback`` attribute to its ``tb`` argument and the
|
||||
``__context__`` and ``__cause__`` attributes to ``None``.
|
||||
|
||||
A new API function, PyErr_SetContext(context), will help C
|
||||
programmers provide chained exception information. This function
|
||||
will first normalize the current exception so it is an instance,
|
||||
then set its '__context__' attribute. A similar API function,
|
||||
PyErr_SetCause(cause), will set the '__cause__' attribute.
|
||||
A new API function, ``PyErr_SetContext(context)``, will help C programmers
|
||||
provide chained exception information. This function will first normalize the
|
||||
current exception so it is an instance, then set its ``__context__``
|
||||
attribute. A similar API function, ``PyErr_SetCause(cause)``, will set the
|
||||
``__cause__`` attribute.
|
||||
|
||||
|
||||
Compatibility
|
||||
=============
|
||||
|
||||
Chained exceptions expose the type of the most recent exception, so
|
||||
they will still match the same 'except' clauses as they do now.
|
||||
Chained exceptions expose the type of the most recent exception, so they will
|
||||
still match the same ``except`` clauses as they do now.
|
||||
|
||||
The proposed changes should not break any code unless it sets or
|
||||
uses attributes named '__context__', '__cause__', or '__traceback__'
|
||||
on exception instances. As of 2005-05-12, the Python standard
|
||||
library contains no mention of such attributes.
|
||||
The proposed changes should not break any code unless it sets or uses
|
||||
attributes named ``__context__``, ``__cause__``, or ``__traceback__`` on
|
||||
exception instances. As of 2005-05-12, the Python standard library contains
|
||||
no mention of such attributes.
|
||||
|
||||
|
||||
Open Issue: Extra Information
|
||||
==============================
|
||||
|
||||
Walter Dörwald [11] expressed a desire to attach extra information
|
||||
to an exception during its upward propagation without changing its
|
||||
type. This could be a useful feature, but it is not addressed by
|
||||
this PEP. It could conceivably be addressed by a separate PEP
|
||||
establishing conventions for other informational attributes on
|
||||
exceptions.
|
||||
Walter Dörwald [11]_ expressed a desire to attach extra information to an
|
||||
exception during its upward propagation without changing its type. This could
|
||||
be a useful feature, but it is not addressed by this PEP. It could
|
||||
conceivably be addressed by a separate PEP establishing conventions for other
|
||||
informational attributes on exceptions.
|
||||
|
||||
|
||||
Open Issue: Suppressing Context
|
||||
================================
|
||||
|
||||
As written, this PEP makes it impossible to suppress '__context__',
|
||||
since setting exc.__context__ to None in an 'except' or 'finally'
|
||||
clause will only result in it being set again when exc is raised.
|
||||
As written, this PEP makes it impossible to suppress ``__context__``, since
|
||||
setting ``exc.__context__`` to ``None`` in an ``except`` or ``finally`` clause
|
||||
will only result in it being set again when ``exc`` is raised.
|
||||
|
||||
|
||||
Open Issue: Limiting Exception Types
|
||||
=====================================
|
||||
|
||||
To improve encapsulation, library implementors may want to wrap all
|
||||
implementation-level exceptions with an application-level exception.
|
||||
One could try to wrap exceptions by writing this:
|
||||
To improve encapsulation, library implementors may want to wrap all
|
||||
implementation-level exceptions with an application-level exception. One could
|
||||
try to wrap exceptions by writing this::
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except:
|
||||
import sys
|
||||
raise ApplicationError from sys.exc_value
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except:
|
||||
import sys
|
||||
raise ApplicationError from sys.exc_value
|
||||
|
||||
or this:
|
||||
or this
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except Exception, exc:
|
||||
raise ApplicationError from exc
|
||||
::
|
||||
|
||||
but both are somewhat flawed. It would be nice to be able to name
|
||||
the current exception in a catch-all 'except' clause, but that isn't
|
||||
addressed here. Such a feature would allow something like this:
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except Exception, exc:
|
||||
raise ApplicationError from exc
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except *, exc:
|
||||
raise ApplicationError from exc
|
||||
but both are somewhat flawed. It would be nice to be able to name the current
|
||||
exception in a catch-all ``except`` clause, but that isn't addressed here.
|
||||
Such a feature would allow something like this::
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except *, exc:
|
||||
raise ApplicationError from exc
|
||||
|
||||
|
||||
Open Issue: yield
|
||||
==================
|
||||
|
||||
The exception context is lost when a 'yield' statement is executed;
|
||||
resuming the frame after the 'yield' does not restore the context.
|
||||
Addressing this problem is out of the scope of this PEP; it is not a
|
||||
new problem, as demonstrated by the following example:
|
||||
The exception context is lost when a ``yield`` statement is executed; resuming
|
||||
the frame after the ``yield`` does not restore the context. Addressing this
|
||||
problem is out of the scope of this PEP; it is not a new problem, as
|
||||
demonstrated by the following example::
|
||||
|
||||
>>> def gen():
|
||||
... try:
|
||||
... 1/0
|
||||
... except:
|
||||
... yield 3
|
||||
... raise
|
||||
...
|
||||
>>> g = gen()
|
||||
>>> g.next()
|
||||
3
|
||||
>>> g.next()
|
||||
TypeError: exceptions must be classes, instances, or strings
|
||||
(deprecated), not NoneType
|
||||
>>> def gen():
|
||||
... try:
|
||||
... 1/0
|
||||
... except:
|
||||
... yield 3
|
||||
... raise
|
||||
...
|
||||
>>> g = gen()
|
||||
>>> g.next()
|
||||
3
|
||||
>>> g.next()
|
||||
TypeError: exceptions must be classes, instances, or strings
|
||||
(deprecated), not NoneType
|
||||
|
||||
|
||||
Open Issue: Garbage Collection
|
||||
===============================
|
||||
|
||||
The strongest objection to this proposal has been that it creates
|
||||
cycles between exceptions and stack frames [12]. Collection of
|
||||
cyclic garbage (and therefore resource release) can be greatly
|
||||
delayed.
|
||||
The strongest objection to this proposal has been that it creates cycles
|
||||
between exceptions and stack frames [12]_. Collection of cyclic garbage (and
|
||||
therefore resource release) can be greatly delayed::
|
||||
|
||||
>>> try:
|
||||
>>> 1/0
|
||||
>>> except Exception, err:
|
||||
>>> pass
|
||||
>>> try:
|
||||
>>> 1/0
|
||||
>>> except Exception, err:
|
||||
>>> pass
|
||||
|
||||
will introduce a cycle from err -> traceback -> stack frame -> err,
|
||||
keeping all locals in the same scope alive until the next GC happens.
|
||||
will introduce a cycle from err -> traceback -> stack frame -> err, keeping
|
||||
all locals in the same scope alive until the next GC happens.
|
||||
|
||||
Today, these locals would go out of scope. There is lots of code
|
||||
which assumes that "local" resources -- particularly open files -- will
|
||||
be closed quickly. If closure has to wait for the next GC, a program
|
||||
(which runs fine today) may run out of file handles.
|
||||
Today, these locals would go out of scope. There is lots of code which
|
||||
assumes that "local" resources -- particularly open files -- will be closed
|
||||
quickly. If closure has to wait for the next GC, a program (which runs fine
|
||||
today) may run out of file handles.
|
||||
|
||||
Making the __traceback__ attribute a weak reference would avoid the
|
||||
problems with cyclic garbage. Unfortunately, it would make saving
|
||||
the Exception for later (as unittest does) more awkward, and it would
|
||||
not allow as much cleanup of the sys module.
|
||||
Making the ``__traceback__`` attribute a weak reference would avoid the
|
||||
problems with cyclic garbage. Unfortunately, it would make saving the
|
||||
``Exception`` for later (as ``unittest`` does) more awkward, and it would not
|
||||
allow as much cleanup of the ``sys`` module.
|
||||
|
||||
A possible alternate solution, suggested by Adam Olsen, would be to
|
||||
instead turn the reference from the stack frame to the 'err' variable
|
||||
into a weak reference when the variable goes out of scope [13].
|
||||
A possible alternate solution, suggested by Adam Olsen, would be to instead
|
||||
turn the reference from the stack frame to the ``err`` variable into a weak
|
||||
reference when the variable goes out of scope [13]_.
|
||||
|
||||
|
||||
Possible Future Compatible Changes
|
||||
==================================
|
||||
|
||||
These changes are consistent with the appearance of exceptions as
|
||||
a single object rather than a triple at the interpreter level.
|
||||
These changes are consistent with the appearance of exceptions as a single
|
||||
object rather than a triple at the interpreter level.
|
||||
|
||||
- If PEP 340 or PEP 343 is accepted, replace the three (type, value,
|
||||
traceback) arguments to __exit__ with a single exception argument.
|
||||
- If PEP 340 or PEP 343 is accepted, replace the three (``type``, ``value``,
|
||||
``traceback``) arguments to ``__exit__`` with a single exception argument.
|
||||
|
||||
- Deprecate sys.exc_type, sys.exc_value, sys.exc_traceback, and
|
||||
sys.exc_info() in favour of a single member, sys.exception.
|
||||
- Deprecate ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
|
||||
``sys.exc_info()`` in favour of a single member, ``sys.exception``.
|
||||
|
||||
- Deprecate sys.last_type, sys.last_value, and sys.last_traceback
|
||||
in favour of a single member, sys.last_exception.
|
||||
- Deprecate ``sys.last_type``, ``sys.last_value``, and ``sys.last_traceback``
|
||||
in favour of a single member, ``sys.last_exception``.
|
||||
|
||||
- Deprecate the three-argument form of the 'raise' statement in
|
||||
favour of the one-argument form.
|
||||
- Deprecate the three-argument form of the ``raise`` statement in favour of
|
||||
the one-argument form.
|
||||
|
||||
- Upgrade cgitb.html() to accept a single value as its first
|
||||
argument as an alternative to a (type, value, traceback) tuple.
|
||||
- Upgrade ``cgitb.html()`` to accept a single value as its first argument as
|
||||
an alternative to a ``(type, value, traceback)`` tuple.
|
||||
|
||||
|
||||
Possible Future Incompatible Changes
|
||||
====================================
|
||||
|
||||
These changes might be worth considering for Python 3000.
|
||||
These changes might be worth considering for Python 3000.
|
||||
|
||||
- Remove sys.exc_type, sys.exc_value, sys.exc_traceback, and
|
||||
sys.exc_info().
|
||||
- Remove ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
|
||||
``sys.exc_info()``.
|
||||
|
||||
- Remove sys.last_type, sys.last_value, and sys.last_traceback.
|
||||
- Remove ``sys.last_type``, ``sys.last_value``, and ``sys.last_traceback``.
|
||||
|
||||
- Replace the three-argument sys.excepthook with a one-argument
|
||||
API, and changing the 'cgitb' module to match.
|
||||
- Replace the three-argument ``sys.excepthook`` with a one-argument API, and
|
||||
changing the ``cgitb`` module to match.
|
||||
|
||||
- Remove the three-argument form of the 'raise' statement.
|
||||
- Remove the three-argument form of the ``raise`` statement.
|
||||
|
||||
- Upgrade traceback.print_exception to accept an 'exception'
|
||||
argument instead of the type, value, and traceback arguments.
|
||||
- Upgrade ``traceback.print_exception`` to accept an ``exception`` argument
|
||||
instead of the ``type``, ``value``, and ``traceback`` arguments.
|
||||
|
||||
|
||||
Acknowledgements
|
||||
================
|
||||
|
||||
Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip
|
||||
J. Eby, Raymond Hettinger, Walter Dörwald, and others.
|
||||
Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip J. Eby,
|
||||
Raymond Hettinger, Walter Dörwald, and others.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
[1] Raymond Hettinger, "Idea for avoiding exception masking"
|
||||
http://mail.python.org/pipermail/python-dev/2003-January/032492.html
|
||||
.. [1] Raymond Hettinger, "Idea for avoiding exception masking"
|
||||
http://mail.python.org/pipermail/python-dev/2003-January/032492.html
|
||||
|
||||
[2] Brett Cannon explains chained exceptions
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036063.html
|
||||
.. [2] Brett Cannon explains chained exceptions
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036063.html
|
||||
|
||||
[3] Greg Ewing points out masking caused by exceptions during finally
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036290.html
|
||||
.. [3] Greg Ewing points out masking caused by exceptions during finally
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036290.html
|
||||
|
||||
[4] Greg Ewing suggests storing the traceback in the exception object
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036092.html
|
||||
.. [4] Greg Ewing suggests storing the traceback in the exception object
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036092.html
|
||||
|
||||
[5] Guido van Rossum mentions exceptions having a traceback attribute
|
||||
http://mail.python.org/pipermail/python-dev/2005-April/053060.html
|
||||
.. [5] Guido van Rossum mentions exceptions having a traceback attribute
|
||||
http://mail.python.org/pipermail/python-dev/2005-April/053060.html
|
||||
|
||||
[6] Ka-Ping Yee, "Tidier Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053671.html
|
||||
.. [6] Ka-Ping Yee, "Tidier Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053671.html
|
||||
|
||||
[7] Ka-Ping Yee, "Chained Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053672.html
|
||||
.. [7] Ka-Ping Yee, "Chained Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053672.html
|
||||
|
||||
[8] Guido van Rossum discusses automatic chaining in PyErr_Set*
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036180.html
|
||||
.. [8] Guido van Rossum discusses automatic chaining in ``PyErr_Set*``
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036180.html
|
||||
|
||||
[9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
|
||||
http://dev.perl.org/perl6/rfc/88.html
|
||||
.. [9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
|
||||
http://dev.perl.org/perl6/rfc/88.html
|
||||
|
||||
[10] MSDN .NET Framework Library, "Exception.InnerException Property"
|
||||
.. [10] MSDN .NET Framework Library, "Exception.InnerException Property"
|
||||
http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemexceptionclassinnerexceptiontopic.asp
|
||||
|
||||
[11] Walter Dörwald suggests wrapping exceptions to add details
|
||||
.. [11] Walter Dörwald suggests wrapping exceptions to add details
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036148.html
|
||||
|
||||
[12] Guido van Rossum restates the objection to cyclic trash
|
||||
.. [12] Guido van Rossum restates the objection to cyclic trash
|
||||
http://mail.python.org/pipermail/python-3000/2007-January/005322.html
|
||||
|
||||
[13] Adam Olsen suggests using a weakref from stack frame to exception
|
||||
.. [13] Adam Olsen suggests using a weakref from stack frame to exception
|
||||
http://mail.python.org/pipermail/python-3000/2007-January/005363.html
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document has been placed in the public domain.
|
||||
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
|
||||
coding: utf-8
|
||||
End:
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 70
|
||||
coding: utf-8
|
||||
End:
|
||||
|
|
742
pep-3134.txt
742
pep-3134.txt
|
@ -5,550 +5,574 @@ Last-Modified: $Date$
|
|||
Author: Ka-Ping Yee
|
||||
Status: Final
|
||||
Type: Standards Track
|
||||
Content-Type: text/plain
|
||||
Content-Type: text/x-rst
|
||||
Created: 12-May-2005
|
||||
Python-Version: 3.0
|
||||
Post-History:
|
||||
|
||||
|
||||
Numbering Note
|
||||
==============
|
||||
|
||||
This PEP started its life as PEP 344. Since it is now targeted
|
||||
for Python 3000, it has been moved into the 3xxx space.
|
||||
This PEP started its life as PEP 344. Since it is now targeted for Python
|
||||
3000, it has been moved into the 3xxx space.
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
This PEP proposes three standard attributes on exception instances:
|
||||
the '__context__' attribute for implicitly chained exceptions, the
|
||||
'__cause__' attribute for explicitly chained exceptions, and the
|
||||
'__traceback__' attribute for the traceback. A new "raise ... from"
|
||||
statement sets the '__cause__' attribute.
|
||||
This PEP proposes three standard attributes on exception instances: the
|
||||
``__context__`` attribute for implicitly chained exceptions, the ``__cause__``
|
||||
attribute for explicitly chained exceptions, and the ``__traceback__``
|
||||
attribute for the traceback. A new ``raise ... from`` statement sets the
|
||||
``__cause__`` attribute.
|
||||
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
During the handling of one exception (exception A), it is possible
|
||||
that another exception (exception B) may occur. In today's Python
|
||||
(version 2.4), if this happens, exception B is propagated outward
|
||||
and exception A is lost. In order to debug the problem, it is
|
||||
useful to know about both exceptions. The '__context__' attribute
|
||||
retains this information automatically.
|
||||
During the handling of one exception (exception A), it is possible that another
|
||||
exception (exception B) may occur. In today's Python (version 2.4), if this
|
||||
happens, exception B is propagated outward and exception A is lost. In order
|
||||
to debug the problem, it is useful to know about both exceptions. The
|
||||
``__context__`` attribute retains this information automatically.
|
||||
|
||||
Sometimes it can be useful for an exception handler to intentionally
|
||||
re-raise an exception, either to provide extra information or to
|
||||
translate an exception to another type. The '__cause__' attribute
|
||||
provides an explicit way to record the direct cause of an exception.
|
||||
Sometimes it can be useful for an exception handler to intentionally re-raise
|
||||
an exception, either to provide extra information or to translate an exception
|
||||
to another type. The ``__cause__`` attribute provides an explicit way to
|
||||
record the direct cause of an exception.
|
||||
|
||||
In today's Python implementation, exceptions are composed of three
|
||||
parts: the type, the value, and the traceback. The 'sys' module,
|
||||
exposes the current exception in three parallel variables, exc_type,
|
||||
exc_value, and exc_traceback, the sys.exc_info() function returns a
|
||||
tuple of these three parts, and the 'raise' statement has a
|
||||
three-argument form accepting these three parts. Manipulating
|
||||
exceptions often requires passing these three things in parallel,
|
||||
which can be tedious and error-prone. Additionally, the 'except'
|
||||
statement can only provide access to the value, not the traceback.
|
||||
Adding the '__traceback__' attribute to exception values makes all
|
||||
the exception information accessible from a single place.
|
||||
In today's Python implementation, exceptions are composed of three parts: the
|
||||
type, the value, and the traceback. The ``sys`` module, exposes the current
|
||||
exception in three parallel variables, ``exc_type``, ``exc_value``, and
|
||||
``exc_traceback``, the ``sys.exc_info()`` function returns a tuple of these
|
||||
three parts, and the ``raise`` statement has a three-argument form accepting
|
||||
these three parts. Manipulating exceptions often requires passing these three
|
||||
things in parallel, which can be tedious and error-prone. Additionally, the
|
||||
``except`` statement can only provide access to the value, not the traceback.
|
||||
Adding the ``__traceback__`` attribute to exception values makes all the
|
||||
exception information accessible from a single place.
|
||||
|
||||
|
||||
History
|
||||
=======
|
||||
|
||||
Raymond Hettinger [1] raised the issue of masked exceptions on
|
||||
Python-Dev in January 2003 and proposed a PyErr_FormatAppend()
|
||||
function that C modules could use to augment the currently active
|
||||
exception with more information. Brett Cannon [2] brought up
|
||||
chained exceptions again in June 2003, prompting a long discussion.
|
||||
Raymond Hettinger [1]_ raised the issue of masked exceptions on Python-Dev in
|
||||
January 2003 and proposed a ``PyErr_FormatAppend()`` function that C modules
|
||||
could use to augment the currently active exception with more information.
|
||||
Brett Cannon [2]_ brought up chained exceptions again in June 2003, prompting
|
||||
a long discussion.
|
||||
|
||||
Greg Ewing [3] identified the case of an exception occurring in a
|
||||
'finally' block during unwinding triggered by an original exception,
|
||||
as distinct from the case of an exception occurring in an 'except'
|
||||
block that is handling the original exception.
|
||||
Greg Ewing [3]_ identified the case of an exception occurring in a ``finally``
|
||||
block during unwinding triggered by an original exception, as distinct from
|
||||
the case of an exception occurring in an ``except`` block that is handling the
|
||||
original exception.
|
||||
|
||||
Greg Ewing [4] and Guido van Rossum [5], and probably others, have
|
||||
previously mentioned adding a traceback attribute to Exception
|
||||
instances. This is noted in PEP 3000.
|
||||
Greg Ewing [4]_ and Guido van Rossum [5]_, and probably others, have
|
||||
previously mentioned adding a traceback attribute to Exception instances.
|
||||
This is noted in PEP 3000.
|
||||
|
||||
This PEP was motivated by yet another recent Python-Dev reposting
|
||||
of the same ideas [6] [7].
|
||||
This PEP was motivated by yet another recent Python-Dev reposting of the same
|
||||
ideas [6]_ [7]_.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
||||
The Python-Dev discussions revealed interest in exception chaining
|
||||
for two quite different purposes. To handle the unexpected raising
|
||||
of a secondary exception, the exception must be retained implicitly.
|
||||
To support intentional translation of an exception, there must be a
|
||||
way to chain exceptions explicitly. This PEP addresses both.
|
||||
The Python-Dev discussions revealed interest in exception chaining for two
|
||||
quite different purposes. To handle the unexpected raising of a secondary
|
||||
exception, the exception must be retained implicitly. To support intentional
|
||||
translation of an exception, there must be a way to chain exceptions
|
||||
explicitly. This PEP addresses both.
|
||||
|
||||
Several attribute names for chained exceptions have been suggested
|
||||
on Python-Dev [2], including 'cause', 'antecedent', 'reason',
|
||||
'original', 'chain', 'chainedexc', 'exc_chain', 'excprev',
|
||||
'previous', and 'precursor'. For an explicitly chained exception,
|
||||
this PEP suggests '__cause__' because of its specific meaning. For
|
||||
an implicitly chained exception, this PEP proposes the name
|
||||
'__context__' because the intended meaning is more specific than
|
||||
temporal precedence but less specific than causation: an exception
|
||||
occurs in the context of handling another exception.
|
||||
Several attribute names for chained exceptions have been suggested on
|
||||
Python-Dev [2]_, including ``cause``, ``antecedent``, ``reason``, ``original``,
|
||||
``chain``, ``chainedexc``, ``exc_chain``, ``excprev``, ``previous``, and
|
||||
``precursor``. For an explicitly chained exception, this PEP suggests
|
||||
``__cause__`` because of its specific meaning. For an implicitly chained
|
||||
exception, this PEP proposes the name ``__context__`` because the intended
|
||||
meaning is more specific than temporal precedence but less specific than
|
||||
causation: an exception occurs in the context of handling another exception.
|
||||
|
||||
This PEP suggests names with leading and trailing double-underscores
|
||||
for these three attributes because they are set by the Python VM.
|
||||
Only in very special cases should they be set by normal assignment.
|
||||
This PEP suggests names with leading and trailing double-underscores for these
|
||||
three attributes because they are set by the Python VM. Only in very special
|
||||
cases should they be set by normal assignment.
|
||||
|
||||
This PEP handles exceptions that occur during 'except' blocks and
|
||||
'finally' blocks in the same way. Reading the traceback makes it
|
||||
clear where the exceptions occurred, so additional mechanisms for
|
||||
distinguishing the two cases would only add unnecessary complexity.
|
||||
This PEP handles exceptions that occur during ``except`` blocks and ``finally``
|
||||
blocks in the same way. Reading the traceback makes it clear where the
|
||||
exceptions occurred, so additional mechanisms for distinguishing the two cases
|
||||
would only add unnecessary complexity.
|
||||
|
||||
This PEP proposes that the outermost exception object (the one
|
||||
exposed for matching by 'except' clauses) be the most recently
|
||||
raised exception for compatibility with current behaviour.
|
||||
This PEP proposes that the outermost exception object (the one exposed for
|
||||
matching by ``except`` clauses) be the most recently raised exception for
|
||||
compatibility with current behaviour.
|
||||
|
||||
This PEP proposes that tracebacks display the outermost exception
|
||||
last, because this would be consistent with the chronological order
|
||||
of tracebacks (from oldest to most recent frame) and because the
|
||||
actual thrown exception is easier to find on the last line.
|
||||
This PEP proposes that tracebacks display the outermost exception last, because
|
||||
this would be consistent with the chronological order of tracebacks (from
|
||||
oldest to most recent frame) and because the actual thrown exception is easier
|
||||
to find on the last line.
|
||||
|
||||
To keep things simpler, the C API calls for setting an exception
|
||||
will not automatically set the exception's '__context__'. Guido
|
||||
van Rossum has expressed concerns with making such changes [8].
|
||||
To keep things simpler, the C API calls for setting an exception will not
|
||||
automatically set the exception's ``__context__``. Guido van Rossum has
|
||||
expressed concerns with making such changes [8]_.
|
||||
|
||||
As for other languages, Java and Ruby both discard the original
|
||||
exception when another exception occurs in a 'catch'/'rescue' or
|
||||
'finally'/'ensure' clause. Perl 5 lacks built-in structured
|
||||
exception handling. For Perl 6, RFC number 88 [9] proposes an exception
|
||||
mechanism that implicitly retains chained exceptions in an array
|
||||
named @@. In that RFC, the most recently raised exception is
|
||||
exposed for matching, as in this PEP; also, arbitrary expressions
|
||||
(possibly involving @@) can be evaluated for exception matching.
|
||||
As for other languages, Java and Ruby both discard the original exception when
|
||||
another exception occurs in a ``catch``/``rescue`` or ``finally``/``ensure``
|
||||
clause. Perl 5 lacks built-in structured exception handling. For Perl 6, RFC
|
||||
number 88 [9]_ proposes an exception mechanism that implicitly retains chained
|
||||
exceptions in an array named ``@@``. In that RFC, the most recently raised
|
||||
exception is exposed for matching, as in this PEP; also, arbitrary expressions
|
||||
(possibly involving ``@@``) can be evaluated for exception matching.
|
||||
|
||||
Exceptions in C# contain a read-only 'InnerException' property that
|
||||
may point to another exception. Its documentation [10] says that
|
||||
"When an exception X is thrown as a direct result of a previous
|
||||
exception Y, the InnerException property of X should contain a
|
||||
reference to Y." This property is not set by the VM automatically;
|
||||
rather, all exception constructors take an optional 'innerException'
|
||||
argument to set it explicitly. The '__cause__' attribute fulfills
|
||||
the same purpose as InnerException, but this PEP proposes a new form
|
||||
of 'raise' rather than extending the constructors of all exceptions.
|
||||
C# also provides a GetBaseException method that jumps directly to
|
||||
the end of the InnerException chain; this PEP proposes no analog.
|
||||
Exceptions in C# contain a read-only ``InnerException`` property that may point
|
||||
to another exception. Its documentation [10]_ says that "When an exception X
|
||||
is thrown as a direct result of a previous exception Y, the ``InnerException``
|
||||
property of X should contain a reference to Y." This property is not set by
|
||||
the VM automatically; rather, all exception constructors take an optional
|
||||
``innerException`` argument to set it explicitly. The ``__cause__`` attribute
|
||||
fulfills the same purpose as ``InnerException``, but this PEP proposes a new
|
||||
form of ``raise`` rather than extending the constructors of all exceptions. C#
|
||||
also provides a ``GetBaseException`` method that jumps directly to the end of
|
||||
the ``InnerException`` chain; this PEP proposes no analog.
|
||||
|
||||
The reason all three of these attributes are presented together in
|
||||
one proposal is that the '__traceback__' attribute provides
|
||||
convenient access to the traceback on chained exceptions.
|
||||
The reason all three of these attributes are presented together in one proposal
|
||||
is that the ``__traceback__`` attribute provides convenient access to the
|
||||
traceback on chained exceptions.
|
||||
|
||||
|
||||
Implicit Exception Chaining
|
||||
===========================
|
||||
|
||||
Here is an example to illustrate the '__context__' attribute.
|
||||
Here is an example to illustrate the ``__context__`` attribute::
|
||||
|
||||
def compute(a, b):
|
||||
try:
|
||||
a/b
|
||||
except Exception, exc:
|
||||
log(exc)
|
||||
def compute(a, b):
|
||||
try:
|
||||
a/b
|
||||
except Exception, exc:
|
||||
log(exc)
|
||||
|
||||
def log(exc):
|
||||
file = open('logfile.txt') # oops, forgot the 'w'
|
||||
print >>file, exc
|
||||
file.close()
|
||||
def log(exc):
|
||||
file = open('logfile.txt') # oops, forgot the 'w'
|
||||
print >>file, exc
|
||||
file.close()
|
||||
|
||||
Calling compute(0, 0) causes a ZeroDivisionError. The compute()
|
||||
function catches this exception and calls log(exc), but the log()
|
||||
function also raises an exception when it tries to write to a
|
||||
file that wasn't opened for writing.
|
||||
Calling ``compute(0, 0)`` causes a ``ZeroDivisionError``. The ``compute()``
|
||||
function catches this exception and calls ``log(exc)``, but the ``log()``
|
||||
function also raises an exception when it tries to write to a file that wasn't
|
||||
opened for writing.
|
||||
|
||||
In today's Python, the caller of compute() gets thrown an IOError.
|
||||
The ZeroDivisionError is lost. With the proposed change, the
|
||||
instance of IOError has an additional '__context__' attribute that
|
||||
retains the ZeroDivisionError.
|
||||
In today's Python, the caller of ``compute()`` gets thrown an ``IOError``. The
|
||||
``ZeroDivisionError`` is lost. With the proposed change, the instance of
|
||||
``IOError`` has an additional ``__context__`` attribute that retains the
|
||||
``ZeroDivisionError``.
|
||||
|
||||
The following more elaborate example demonstrates the handling of a
|
||||
mixture of 'finally' and 'except' clauses:
|
||||
The following more elaborate example demonstrates the handling of a mixture of
|
||||
``finally`` and ``except`` clauses::
|
||||
|
||||
def main(filename):
|
||||
file = open(filename) # oops, forgot the 'w'
|
||||
def main(filename):
|
||||
file = open(filename) # oops, forgot the 'w'
|
||||
try:
|
||||
try:
|
||||
compute()
|
||||
except Exception, exc:
|
||||
log(file, exc)
|
||||
finally:
|
||||
file.clos() # oops, misspelled 'close'
|
||||
file.clos() # oops, misspelled 'close'
|
||||
|
||||
def compute():
|
||||
1/0
|
||||
def compute():
|
||||
1/0
|
||||
|
||||
def log(file, exc):
|
||||
try:
|
||||
print >>file, exc # oops, file is not writable
|
||||
except:
|
||||
display(exc)
|
||||
def log(file, exc):
|
||||
try:
|
||||
print >>file, exc # oops, file is not writable
|
||||
except:
|
||||
display(exc)
|
||||
|
||||
def display(exc):
|
||||
print ex # oops, misspelled 'exc'
|
||||
def display(exc):
|
||||
print ex # oops, misspelled 'exc'
|
||||
|
||||
Calling main() with the name of an existing file will trigger four
|
||||
exceptions. The ultimate result will be an AttributeError due to
|
||||
the misspelling of 'clos', whose __context__ points to a NameError
|
||||
due to the misspelling of 'ex', whose __context__ points to an
|
||||
IOError due to the file being read-only, whose __context__ points to
|
||||
a ZeroDivisionError, whose __context__ attribute is None.
|
||||
Calling ``main()`` with the name of an existing file will trigger four
|
||||
exceptions. The ultimate result will be an ``AttributeError`` due to the
|
||||
misspelling of ``clos``, whose ``__context__`` points to a ``NameError`` due
|
||||
to the misspelling of ``ex``, whose ``__context__`` points to an ``IOError``
|
||||
due to the file being read-only, whose ``__context__`` points to a
|
||||
``ZeroDivisionError``, whose ``__context__`` attribute is ``None``.
|
||||
|
||||
The proposed semantics are as follows:
|
||||
The proposed semantics are as follows:
|
||||
|
||||
1. Each thread has an exception context initially set to None.
|
||||
1. Each thread has an exception context initially set to ``None``.
|
||||
|
||||
2. Whenever an exception is raised, if the exception instance does
|
||||
not already have a '__context__' attribute, the interpreter sets
|
||||
it equal to the thread's exception context.
|
||||
2. Whenever an exception is raised, if the exception instance does not already
|
||||
have a ``__context__`` attribute, the interpreter sets it equal to the
|
||||
thread's exception context.
|
||||
|
||||
3. Immediately after an exception is raised, the thread's exception
|
||||
context is set to the exception.
|
||||
3. Immediately after an exception is raised, the thread's exception context is
|
||||
set to the exception.
|
||||
|
||||
4. Whenever the interpreter exits an 'except' block by reaching the
|
||||
end or executing a 'return', 'yield', 'continue', or 'break'
|
||||
statement, the thread's exception context is set to None.
|
||||
4. Whenever the interpreter exits an ``except`` block by reaching the end or
|
||||
executing a ``return``, ``yield``, ``continue``, or ``break`` statement, the
|
||||
thread's exception context is set to ``None``.
|
||||
|
||||
|
||||
Explicit Exception Chaining
|
||||
===========================
|
||||
|
||||
The '__cause__' attribute on exception objects is always initialized
|
||||
to None. It is set by a new form of the 'raise' statement:
|
||||
The ``__cause__`` attribute on exception objects is always initialized to
|
||||
``None``. It is set by a new form of the ``raise`` statement::
|
||||
|
||||
raise EXCEPTION from CAUSE
|
||||
raise EXCEPTION from CAUSE
|
||||
|
||||
which is equivalent to:
|
||||
which is equivalent to::
|
||||
|
||||
exc = EXCEPTION
|
||||
exc.__cause__ = CAUSE
|
||||
raise exc
|
||||
exc = EXCEPTION
|
||||
exc.__cause__ = CAUSE
|
||||
raise exc
|
||||
|
||||
In the following example, a database provides implementations for a
|
||||
few different kinds of storage, with file storage as one kind. The
|
||||
database designer wants errors to propagate as DatabaseError objects
|
||||
so that the client doesn't have to be aware of the storage-specific
|
||||
details, but doesn't want to lose the underlying error information.
|
||||
In the following example, a database provides implementations for a few
|
||||
different kinds of storage, with file storage as one kind. The database
|
||||
designer wants errors to propagate as ``DatabaseError`` objects so that the
|
||||
client doesn't have to be aware of the storage-specific details, but doesn't
|
||||
want to lose the underlying error information.
|
||||
|
||||
class DatabaseError(Exception):
|
||||
pass
|
||||
::
|
||||
|
||||
class FileDatabase(Database):
|
||||
def __init__(self, filename):
|
||||
try:
|
||||
self.file = open(filename)
|
||||
except IOError, exc:
|
||||
raise DatabaseError('failed to open') from exc
|
||||
class DatabaseError(Exception):
|
||||
pass
|
||||
|
||||
If the call to open() raises an exception, the problem will be
|
||||
reported as a DatabaseError, with a __cause__ attribute that reveals
|
||||
the IOError as the original cause.
|
||||
class FileDatabase(Database):
|
||||
def __init__(self, filename):
|
||||
try:
|
||||
self.file = open(filename)
|
||||
except IOError, exc:
|
||||
raise DatabaseError('failed to open') from exc
|
||||
|
||||
If the call to ``open()`` raises an exception, the problem will be reported as
|
||||
a ``DatabaseError``, with a ``__cause__`` attribute that reveals the
|
||||
``IOError`` as the original cause.
|
||||
|
||||
|
||||
Traceback Attribute
|
||||
===================
|
||||
|
||||
The following example illustrates the '__traceback__' attribute.
|
||||
The following example illustrates the ``__traceback__`` attribute.
|
||||
|
||||
def do_logged(file, work):
|
||||
try:
|
||||
work()
|
||||
except Exception, exc:
|
||||
write_exception(file, exc)
|
||||
raise exc
|
||||
::
|
||||
|
||||
from traceback import format_tb
|
||||
def do_logged(file, work):
|
||||
try:
|
||||
work()
|
||||
except Exception, exc:
|
||||
write_exception(file, exc)
|
||||
raise exc
|
||||
|
||||
def write_exception(file, exc):
|
||||
...
|
||||
type = exc.__class__
|
||||
message = str(exc)
|
||||
lines = format_tb(exc.__traceback__)
|
||||
file.write(... type ... message ... lines ...)
|
||||
...
|
||||
from traceback import format_tb
|
||||
|
||||
In today's Python, the do_logged() function would have to extract
|
||||
the traceback from sys.exc_traceback or sys.exc_info()[2] and pass
|
||||
both the value and the traceback to write_exception(). With the
|
||||
proposed change, write_exception() simply gets one argument and
|
||||
obtains the exception using the '__traceback__' attribute.
|
||||
def write_exception(file, exc):
|
||||
...
|
||||
type = exc.__class__
|
||||
message = str(exc)
|
||||
lines = format_tb(exc.__traceback__)
|
||||
file.write(... type ... message ... lines ...)
|
||||
...
|
||||
|
||||
The proposed semantics are as follows:
|
||||
In today's Python, the ``do_logged()`` function would have to extract the
|
||||
traceback from ``sys.exc_traceback`` or ``sys.exc_info()`` [2]_ and pass both
|
||||
the value and the traceback to ``write_exception()``. With the proposed
|
||||
change, ``write_exception()`` simply gets one argument and obtains the
|
||||
exception using the ``__traceback__`` attribute.
|
||||
|
||||
1. Whenever an exception is caught, if the exception instance does
|
||||
not already have a '__traceback__' attribute, the interpreter
|
||||
sets it to the newly caught traceback.
|
||||
The proposed semantics are as follows:
|
||||
|
||||
1. Whenever an exception is caught, if the exception instance does not already
|
||||
have a ``__traceback__`` attribute, the interpreter sets it to the newly
|
||||
caught traceback.
|
||||
|
||||
|
||||
Enhanced Reporting
|
||||
==================
|
||||
|
||||
The default exception handler will be modified to report chained
|
||||
exceptions. The chain of exceptions is traversed by following the
|
||||
'__cause__' and '__context__' attributes, with '__cause__' taking
|
||||
priority. In keeping with the chronological order of tracebacks,
|
||||
the most recently raised exception is displayed last; that is, the
|
||||
display begins with the description of the innermost exception and
|
||||
backs up the chain to the outermost exception. The tracebacks are
|
||||
formatted as usual, with one of the lines:
|
||||
The default exception handler will be modified to report chained exceptions.
|
||||
The chain of exceptions is traversed by following the ``__cause__`` and
|
||||
``__context__`` attributes, with ``__cause__`` taking priority. In keeping
|
||||
with the chronological order of tracebacks, the most recently raised exception
|
||||
is displayed last; that is, the display begins with the description of the
|
||||
innermost exception and backs up the chain to the outermost exception. The
|
||||
tracebacks are formatted as usual, with one of the lines::
|
||||
|
||||
The above exception was the direct cause of the following exception:
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
or
|
||||
or
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
::
|
||||
|
||||
between tracebacks, depending whether they are linked by __cause__
|
||||
or __context__ respectively. Here is a sketch of the procedure:
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
def print_chain(exc):
|
||||
if exc.__cause__:
|
||||
print_chain(exc.__cause__)
|
||||
print '\nThe above exception was the direct cause...'
|
||||
elif exc.__context__:
|
||||
print_chain(exc.__context__)
|
||||
print '\nDuring handling of the above exception, ...'
|
||||
print_exc(exc)
|
||||
between tracebacks, depending whether they are linked by ``__cause__`` or
|
||||
``__context__`` respectively. Here is a sketch of the procedure::
|
||||
|
||||
In the 'traceback' module, the format_exception, print_exception,
|
||||
print_exc, and print_last functions will be updated to accept an
|
||||
optional 'chain' argument, True by default. When this argument is
|
||||
True, these functions will format or display the entire chain of
|
||||
exceptions as just described. When it is False, these functions
|
||||
will format or display only the outermost exception.
|
||||
def print_chain(exc):
|
||||
if exc.__cause__:
|
||||
print_chain(exc.__cause__)
|
||||
print '\nThe above exception was the direct cause...'
|
||||
elif exc.__context__:
|
||||
print_chain(exc.__context__)
|
||||
print '\nDuring handling of the above exception, ...'
|
||||
print_exc(exc)
|
||||
|
||||
The 'cgitb' module should also be updated to display the entire
|
||||
chain of exceptions.
|
||||
In the ``traceback`` module, the ``format_exception``, ``print_exception``,
|
||||
``print_exc``, and ``print_last`` functions will be updated to accept an
|
||||
optional ``chain`` argument, ``True`` by default. When this argument is
|
||||
``True``, these functions will format or display the entire chain of exceptions
|
||||
as just described. When it is ``False``, these functions will format or
|
||||
display only the outermost exception.
|
||||
|
||||
The ``cgitb`` module should also be updated to display the entire chain of
|
||||
exceptions.
|
||||
|
||||
|
||||
C API
|
||||
=====
|
||||
|
||||
The PyErr_Set* calls for setting exceptions will not set the
|
||||
'__context__' attribute on exceptions. PyErr_NormalizeException
|
||||
will always set the 'traceback' attribute to its 'tb' argument and
|
||||
the '__context__' and '__cause__' attributes to None.
|
||||
The ``PyErr_Set*`` calls for setting exceptions will not set the
|
||||
``__context__`` attribute on exceptions. ``PyErr_NormalizeException`` will
|
||||
always set the ``traceback`` attribute to its ``tb`` argument and the
|
||||
``__context__`` and ``__cause__`` attributes to ``None``.
|
||||
|
||||
A new API function, PyErr_SetContext(context), will help C
|
||||
programmers provide chained exception information. This function
|
||||
will first normalize the current exception so it is an instance,
|
||||
then set its '__context__' attribute. A similar API function,
|
||||
PyErr_SetCause(cause), will set the '__cause__' attribute.
|
||||
A new API function, ``PyErr_SetContext(context)``, will help C programmers
|
||||
provide chained exception information. This function will first normalize the
|
||||
current exception so it is an instance, then set its ``__context__`` attribute.
|
||||
A similar API function, ``PyErr_SetCause(cause)``, will set the ``__cause__``
|
||||
attribute.
|
||||
|
||||
|
||||
Compatibility
|
||||
=============
|
||||
|
||||
Chained exceptions expose the type of the most recent exception, so
|
||||
they will still match the same 'except' clauses as they do now.
|
||||
Chained exceptions expose the type of the most recent exception, so they will
|
||||
still match the same ``except`` clauses as they do now.
|
||||
|
||||
The proposed changes should not break any code unless it sets or
|
||||
uses attributes named '__context__', '__cause__', or '__traceback__'
|
||||
on exception instances. As of 2005-05-12, the Python standard
|
||||
library contains no mention of such attributes.
|
||||
The proposed changes should not break any code unless it sets or uses
|
||||
attributes named ``__context__``, ``__cause__``, or ``__traceback__`` on
|
||||
exception instances. As of 2005-05-12, the Python standard library contains no
|
||||
mention of such attributes.
|
||||
|
||||
|
||||
Open Issue: Extra Information
|
||||
==============================
|
||||
|
||||
Walter Dörwald [11] expressed a desire to attach extra information
|
||||
to an exception during its upward propagation without changing its
|
||||
type. This could be a useful feature, but it is not addressed by
|
||||
this PEP. It could conceivably be addressed by a separate PEP
|
||||
establishing conventions for other informational attributes on
|
||||
exceptions.
|
||||
Walter Dörwald [11]_ expressed a desire to attach extra information to an
|
||||
exception during its upward propagation without changing its type. This could
|
||||
be a useful feature, but it is not addressed by this PEP. It could conceivably
|
||||
be addressed by a separate PEP establishing conventions for other informational
|
||||
attributes on exceptions.
|
||||
|
||||
|
||||
Open Issue: Suppressing Context
|
||||
================================
|
||||
|
||||
As written, this PEP makes it impossible to suppress '__context__',
|
||||
since setting exc.__context__ to None in an 'except' or 'finally'
|
||||
clause will only result in it being set again when exc is raised.
|
||||
As written, this PEP makes it impossible to suppress ``__context__``, since
|
||||
setting ``exc.__context__`` to ``None`` in an ``except`` or ``finally`` clause
|
||||
will only result in it being set again when ``exc`` is raised.
|
||||
|
||||
|
||||
Open Issue: Limiting Exception Types
|
||||
=====================================
|
||||
|
||||
To improve encapsulation, library implementors may want to wrap all
|
||||
implementation-level exceptions with an application-level exception.
|
||||
One could try to wrap exceptions by writing this:
|
||||
To improve encapsulation, library implementors may want to wrap all
|
||||
implementation-level exceptions with an application-level exception. One could
|
||||
try to wrap exceptions by writing this::
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except:
|
||||
import sys
|
||||
raise ApplicationError from sys.exc_value
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except:
|
||||
import sys
|
||||
raise ApplicationError from sys.exc_value
|
||||
|
||||
or this:
|
||||
or this::
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except Exception, exc:
|
||||
raise ApplicationError from exc
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except Exception, exc:
|
||||
raise ApplicationError from exc
|
||||
|
||||
but both are somewhat flawed. It would be nice to be able to name
|
||||
the current exception in a catch-all 'except' clause, but that isn't
|
||||
addressed here. Such a feature would allow something like this:
|
||||
but both are somewhat flawed. It would be nice to be able to name the current
|
||||
exception in a catch-all ``except`` clause, but that isn't addressed here.
|
||||
Such a feature would allow something like this::
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except *, exc:
|
||||
raise ApplicationError from exc
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except *, exc:
|
||||
raise ApplicationError from exc
|
||||
|
||||
|
||||
Open Issue: yield
|
||||
==================
|
||||
|
||||
The exception context is lost when a 'yield' statement is executed;
|
||||
resuming the frame after the 'yield' does not restore the context.
|
||||
Addressing this problem is out of the scope of this PEP; it is not a
|
||||
new problem, as demonstrated by the following example:
|
||||
The exception context is lost when a ``yield`` statement is executed; resuming
|
||||
the frame after the ``yield`` does not restore the context. Addressing this
|
||||
problem is out of the scope of this PEP; it is not a new problem, as
|
||||
demonstrated by the following example::
|
||||
|
||||
>>> def gen():
|
||||
... try:
|
||||
... 1/0
|
||||
... except:
|
||||
... yield 3
|
||||
... raise
|
||||
...
|
||||
>>> g = gen()
|
||||
>>> g.next()
|
||||
3
|
||||
>>> g.next()
|
||||
TypeError: exceptions must be classes, instances, or strings
|
||||
(deprecated), not NoneType
|
||||
>>> def gen():
|
||||
... try:
|
||||
... 1/0
|
||||
... except:
|
||||
... yield 3
|
||||
... raise
|
||||
...
|
||||
>>> g = gen()
|
||||
>>> g.next()
|
||||
3
|
||||
>>> g.next()
|
||||
TypeError: exceptions must be classes, instances, or strings
|
||||
(deprecated), not NoneType
|
||||
|
||||
|
||||
Open Issue: Garbage Collection
|
||||
===============================
|
||||
|
||||
The strongest objection to this proposal has been that it creates
|
||||
cycles between exceptions and stack frames [12]. Collection of
|
||||
cyclic garbage (and therefore resource release) can be greatly
|
||||
delayed.
|
||||
The strongest objection to this proposal has been that it creates cycles
|
||||
between exceptions and stack frames [12]_. Collection of cyclic garbage (and
|
||||
therefore resource release) can be greatly delayed.
|
||||
|
||||
>>> try:
|
||||
>>> 1/0
|
||||
>>> except Exception, err:
|
||||
>>> pass
|
||||
::
|
||||
|
||||
will introduce a cycle from err -> traceback -> stack frame -> err,
|
||||
keeping all locals in the same scope alive until the next GC happens.
|
||||
>>> try:
|
||||
>>> 1/0
|
||||
>>> except Exception, err:
|
||||
>>> pass
|
||||
|
||||
Today, these locals would go out of scope. There is lots of code
|
||||
which assumes that "local" resources -- particularly open files -- will
|
||||
be closed quickly. If closure has to wait for the next GC, a program
|
||||
(which runs fine today) may run out of file handles.
|
||||
will introduce a cycle from err -> traceback -> stack frame -> err, keeping all
|
||||
locals in the same scope alive until the next GC happens.
|
||||
|
||||
Making the __traceback__ attribute a weak reference would avoid the
|
||||
problems with cyclic garbage. Unfortunately, it would make saving
|
||||
the Exception for later (as unittest does) more awkward, and it would
|
||||
not allow as much cleanup of the sys module.
|
||||
Today, these locals would go out of scope. There is lots of code which assumes
|
||||
that "local" resources -- particularly open files -- will be closed quickly.
|
||||
If closure has to wait for the next GC, a program (which runs fine today) may
|
||||
run out of file handles.
|
||||
|
||||
A possible alternate solution, suggested by Adam Olsen, would be to
|
||||
instead turn the reference from the stack frame to the 'err' variable
|
||||
into a weak reference when the variable goes out of scope [13].
|
||||
Making the ``__traceback__`` attribute a weak reference would avoid the
|
||||
problems with cyclic garbage. Unfortunately, it would make saving the
|
||||
``Exception`` for later (as ``unittest`` does) more awkward, and it would not
|
||||
allow as much cleanup of the ``sys`` module.
|
||||
|
||||
A possible alternate solution, suggested by Adam Olsen, would be to instead
|
||||
turn the reference from the stack frame to the ``err`` variable into a weak
|
||||
reference when the variable goes out of scope [13]_.
|
||||
|
||||
|
||||
Possible Future Compatible Changes
|
||||
==================================
|
||||
|
||||
These changes are consistent with the appearance of exceptions as
|
||||
a single object rather than a triple at the interpreter level.
|
||||
These changes are consistent with the appearance of exceptions as a single
|
||||
object rather than a triple at the interpreter level.
|
||||
|
||||
- If PEP 340 or PEP 343 is accepted, replace the three (type, value,
|
||||
traceback) arguments to __exit__ with a single exception argument.
|
||||
- If PEP 340 or PEP 343 is accepted, replace the three (``type``, ``value``,
|
||||
``traceback``) arguments to ``__exit__`` with a single exception argument.
|
||||
|
||||
- Deprecate sys.exc_type, sys.exc_value, sys.exc_traceback, and
|
||||
sys.exc_info() in favour of a single member, sys.exception.
|
||||
- Deprecate ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
|
||||
``sys.exc_info()`` in favour of a single member, ``sys.exception``.
|
||||
|
||||
- Deprecate sys.last_type, sys.last_value, and sys.last_traceback
|
||||
in favour of a single member, sys.last_exception.
|
||||
- Deprecate ``sys.last_type``, ``sys.last_value``, and ``sys.last_traceback``
|
||||
in favour of a single member, ``sys.last_exception``.
|
||||
|
||||
- Deprecate the three-argument form of the 'raise' statement in
|
||||
favour of the one-argument form.
|
||||
- Deprecate the three-argument form of the ``raise`` statement in favour of the
|
||||
one-argument form.
|
||||
|
||||
- Upgrade cgitb.html() to accept a single value as its first
|
||||
argument as an alternative to a (type, value, traceback) tuple.
|
||||
- Upgrade ``cgitb.html()`` to accept a single value as its first argument as an
|
||||
alternative to a ``(type, value, traceback)`` tuple.
|
||||
|
||||
|
||||
Possible Future Incompatible Changes
|
||||
====================================
|
||||
|
||||
These changes might be worth considering for Python 3000.
|
||||
These changes might be worth considering for Python 3000.
|
||||
|
||||
- Remove sys.exc_type, sys.exc_value, sys.exc_traceback, and
|
||||
sys.exc_info().
|
||||
- Remove ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
|
||||
``sys.exc_info()``.
|
||||
|
||||
- Remove sys.last_type, sys.last_value, and sys.last_traceback.
|
||||
- Remove ``sys.last_type``, ``sys.last_value``, and ``sys.last_traceback``.
|
||||
|
||||
- Replace the three-argument sys.excepthook with a one-argument
|
||||
API, and changing the 'cgitb' module to match.
|
||||
- Replace the three-argument ``sys.excepthook`` with a one-argument API, and
|
||||
changing the ``cgitb`` module to match.
|
||||
|
||||
- Remove the three-argument form of the 'raise' statement.
|
||||
- Remove the three-argument form of the ``raise`` statement.
|
||||
|
||||
- Upgrade traceback.print_exception to accept an 'exception'
|
||||
argument instead of the type, value, and traceback arguments.
|
||||
- Upgrade ``traceback.print_exception`` to accept an ``exception`` argument
|
||||
instead of the ``type``, ``value``, and ``traceback`` arguments.
|
||||
|
||||
|
||||
Implementation
|
||||
==============
|
||||
|
||||
The __traceback__ and __cause__ attributes and the new raise syntax were
|
||||
implemented in revision 57783 [14].
|
||||
The ``__traceback__`` and ``__cause__`` attributes and the new raise syntax
|
||||
were implemented in revision 57783 [14]_.
|
||||
|
||||
|
||||
Acknowledgements
|
||||
================
|
||||
|
||||
Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip
|
||||
J. Eby, Raymond Hettinger, Walter Dörwald, and others.
|
||||
Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip J. Eby,
|
||||
Raymond Hettinger, Walter Dörwald, and others.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
[1] Raymond Hettinger, "Idea for avoiding exception masking"
|
||||
http://mail.python.org/pipermail/python-dev/2003-January/032492.html
|
||||
.. [1] Raymond Hettinger, "Idea for avoiding exception masking"
|
||||
http://mail.python.org/pipermail/python-dev/2003-January/032492.html
|
||||
|
||||
[2] Brett Cannon explains chained exceptions
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036063.html
|
||||
.. [2] Brett Cannon explains chained exceptions
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036063.html
|
||||
|
||||
[3] Greg Ewing points out masking caused by exceptions during finally
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036290.html
|
||||
.. [3] Greg Ewing points out masking caused by exceptions during finally
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036290.html
|
||||
|
||||
[4] Greg Ewing suggests storing the traceback in the exception object
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036092.html
|
||||
.. [4] Greg Ewing suggests storing the traceback in the exception object
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036092.html
|
||||
|
||||
[5] Guido van Rossum mentions exceptions having a traceback attribute
|
||||
http://mail.python.org/pipermail/python-dev/2005-April/053060.html
|
||||
.. [5] Guido van Rossum mentions exceptions having a traceback attribute
|
||||
http://mail.python.org/pipermail/python-dev/2005-April/053060.html
|
||||
|
||||
[6] Ka-Ping Yee, "Tidier Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053671.html
|
||||
.. [6] Ka-Ping Yee, "Tidier Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053671.html
|
||||
|
||||
[7] Ka-Ping Yee, "Chained Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053672.html
|
||||
.. [7] Ka-Ping Yee, "Chained Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053672.html
|
||||
|
||||
[8] Guido van Rossum discusses automatic chaining in PyErr_Set*
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036180.html
|
||||
.. [8] Guido van Rossum discusses automatic chaining in ``PyErr_Set*``
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036180.html
|
||||
|
||||
[9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
|
||||
http://dev.perl.org/perl6/rfc/88.html
|
||||
.. [9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
|
||||
http://dev.perl.org/perl6/rfc/88.html
|
||||
|
||||
[10] MSDN .NET Framework Library, "Exception.InnerException Property"
|
||||
.. [10] MSDN .NET Framework Library, "Exception.InnerException Property"
|
||||
http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemexceptionclassinnerexceptiontopic.asp
|
||||
|
||||
[11] Walter Dörwald suggests wrapping exceptions to add details
|
||||
.. [11] Walter Dörwald suggests wrapping exceptions to add details
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036148.html
|
||||
|
||||
[12] Guido van Rossum restates the objection to cyclic trash
|
||||
.. [12] Guido van Rossum restates the objection to cyclic trash
|
||||
http://mail.python.org/pipermail/python-3000/2007-January/005322.html
|
||||
|
||||
[13] Adam Olsen suggests using a weakref from stack frame to exception
|
||||
.. [13] Adam Olsen suggests using a weakref from stack frame to exception
|
||||
http://mail.python.org/pipermail/python-3000/2007-January/005363.html
|
||||
|
||||
[14] Patch to implement the bulk of the PEP
|
||||
.. [14] Patch to implement the bulk of the PEP
|
||||
http://svn.python.org/view/python/branches/py3k/Include/?rev=57783&view=rev
|
||||
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document has been placed in the public domain.
|
||||
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
|
||||
coding: utf-8
|
||||
End:
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 70
|
||||
coding: utf-8
|
||||
End:
|
||||
|
|
Loading…
Reference in New Issue