reSTify 5 PEPs (#276)

This commit is contained in:
csabella 2017-06-02 14:46:13 -04:00 committed by Brett Cannon
parent 02969ebbd2
commit 329ed7e935
5 changed files with 1597 additions and 1535 deletions

View File

@ -5,369 +5,375 @@ Last-Modified: $Date$
Author: Tim Peters <tim.peters@gmail.com> Author: Tim Peters <tim.peters@gmail.com>
Status: Final Status: Final
Type: Standards Track Type: Standards Track
Content-Type: text/x-rst
Created: 26-Feb-2001 Created: 26-Feb-2001
Python-Version: 2.1 Python-Version: 2.1
Post-History: 26-Feb-2001 Post-History: 26-Feb-2001
Motivation Motivation
==========
From time to time, Python makes an incompatible change to the From time to time, Python makes an incompatible change to the advertised
advertised semantics of core language constructs, or changes their semantics of core language constructs, or changes their accidental
accidental (implementation-dependent) behavior in some way. While this (implementation-dependent) behavior in some way. While this is never done
is never done capriciously, and is always done with the aim of capriciously, and is always done with the aim of improving the language over
improving the language over the long term, over the short term it's the long term, over the short term it's contentious and disrupting.
contentious and disrupting.
PEP 5, Guidelines for Language Evolution[1] suggests ways to ease PEP 5, Guidelines for Language Evolution [1]_ suggests ways to ease the pain,
the pain, and this PEP introduces some machinery in support of that. and this PEP introduces some machinery in support of that.
PEP 227, Statically Nested Scopes[2] is the first application, and PEP 227, Statically Nested Scopes [2]_ is the first application, and will be
will be used as an example here. used as an example here.
Intent 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 When an incompatible change to core language syntax or semantics is being
being made: made:
1. The release C that introduces the change does not change the 1. The release C that introduces the change does not change the syntax or
syntax or semantics by default. semantics by default.
2. A future release R is identified in which the new syntax or semantics 2. A future release R is identified in which the new syntax or semantics will
will be enforced. be enforced.
3. The mechanisms described in PEP 3, Warning Framework[3] are 3. The mechanisms described in PEP 3, Warning Framework [3]_ are used to
used to generate warnings, whenever possible, about constructs generate warnings, whenever possible, about constructs or operations whose
or operations whose meaning may[4] change in release R. meaning may [4]_ change in release R.
4. The new future_statement (see below) can be explicitly included in a 4. The new future_statement (see below) can be explicitly included in a module
module M to request that the code in module M use the new syntax or M to request that the code in module M use the new syntax or semantics in
semantics in the current release C. the current release C.
So old code continues to work by default, for at least one release, So old code continues to work by default, for at least one release, although
although it may start to generate new warning messages. Migration to it may start to generate new warning messages. Migration to the new syntax or
the new syntax or semantics can proceed during that time, using the semantics can proceed during that time, using the future_statement to make
future_statement to make modules containing it act as if the new syntax modules containing it act as if the new syntax or semantics were already being
or semantics were already being enforced. enforced.
Note that there is no need to involve the future_statement machinery Note that there is no need to involve the future_statement machinery in new
in new features unless they can break existing code; fully backward- features unless they can break existing code; fully backward- compatible
compatible additions can-- and should --be introduced without a additions can-- and should --be introduced without a corresponding
corresponding future_statement. future_statement.
Syntax Syntax
======
A future_statement is simply a from/import statement using the reserved A future_statement is simply a from/import statement using the reserved module
module name __future__: name ``__future__``::
future_statement: "from" "__future__" "import" feature ["as" name] future_statement: "from" "__future__" "import" feature ["as" name]
("," feature ["as" name])* (","feature ["as" name])*
feature: identifier feature: identifier
name: identifier name: identifier
In addition, all future_statments must appear near the top of the In addition, all future_statments must appear near the top of the module. The
module. The only lines that can appear before a future_statement are: only lines that can appear before a future_statement are:
+ The module docstring (if any). + The module docstring (if any).
+ Comments. + Comments.
+ Blank lines. + Blank lines.
+ Other future_statements. + Other future_statements.
Example: Example::
"""This is a module docstring."""
# This is a comment, preceded by a blank line and followed by """This is a module docstring."""
# a future_statement.
from __future__ import nested_scopes
from math import sin # This is a comment, preceded by a blank line and followed by
from __future__ import alabaster_weenoblobs # compile-time error! # a future_statement.
# That was an error because preceded by a non-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 Semantics
=========
A future_statement is recognized and treated specially at compile time: A future_statement is recognized and treated specially at compile time:
changes to the semantics of core constructs are often implemented by changes to the semantics of core constructs are often implemented by
generating different code. It may even be the case that a new feature generating different code. It may even be the case that a new feature
introduces new incompatible syntax (such as a new reserved word), in introduces new incompatible syntax (such as a new reserved word), in which
which case the compiler may need to parse the module differently. Such case the compiler may need to parse the module differently. Such decisions
decisions cannot be pushed off until runtime. cannot be pushed off until runtime.
For any given release, the compiler knows which feature names have been For any given release, the compiler knows which feature names have been
defined, and raises a compile-time error if a future_statement contains defined, and raises a compile-time error if a future_statement contains a
a feature not known to it[5]. feature not known to it [5]_.
The direct runtime semantics are the same as for any import statement: The direct runtime semantics are the same as for any ``import`` statement:
there is a standard module __future__.py, described later, and it will there is a standard module ``__future__.py``, described later, and it will be
be imported in the usual way at the time the future_statement is imported in the usual way at the time the future_statement is executed.
executed.
The *interesting* runtime semantics depend on the specific feature(s) The *interesting* runtime semantics depend on the specific feature(s)
"imported" by the future_statement(s) appearing in the module. "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 That is not a future_statement; it's an ordinary import statement, with no
no special semantics or syntax restrictions. special semantics or syntax restrictions.
Example Example
=======
Consider this code, in file scope.py: Consider this code, in file scope.py::
x = 42 x = 42
def f(): def f():
x = 666 x = 666
def g(): def g():
print "x is", x print "x is", x
g() g()
f() 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 Nested scopes [2]_ are being introduced in 2.1. But under 2.1, it still
prints 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 In 2.2, and also in 2.1 *if* ``from __future__ import nested_scopes`` is
included at the top of scope.py, it prints included at the top of ``scope.py``, it prints::
x is 666 x is 666
Standard Module __future__.py 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 1. To avoid confusing existing tools that analyze import statements and expect
expect to find the modules they're importing. to find the modules they're importing.
2. To ensure that future_statements run under releases prior to 2.1 2. To ensure that future_statements run under releases prior to 2.1 at least
at least yield runtime exceptions (the import of __future__ will yield runtime exceptions (the import of ``__future__`` will fail, because
fail, because there was no module of that name prior to 2.1). there was no module of that name prior to 2.1).
3. To document when incompatible changes were introduced, and when they 3. To document when incompatible changes were introduced, and when they will
will be-- or were --made mandatory. This is a form of executable be-- or were --made mandatory. This is a form of executable documentation,
documentation, and can be inspected programatically via importing and can be inspected programatically via importing ``__future__`` and
__future__ and examining its contents. 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 where, normally, *OptionalRelease* < *MandatoryRelease*, and both are
5-tuples of the same form as sys.version_info: 5-tuples of the same form as ``sys.version_info``::
(PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int (PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int
PY_MINOR_VERSION, # the 1; an int PY_MINOR_VERSION, # the 1; an int
PY_MICRO_VERSION, # the 0; an int PY_MICRO_VERSION, # the 0; an int
PY_RELEASE_LEVEL, # "alpha", "beta", "candidate" or "final"; string 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, In the case of *MandatoryReleases* that have not yet occurred,
MandatoryRelease predicts the release in which the feature will become *MandatoryRelease* predicts the release in which the feature will become part
part of the language. of the language.
Else MandatoryRelease records when the feature became part of the Else *MandatoryRelease* records when the feature became part of the language;
language; in releases at or after that, modules no longer need 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 *MandatoryRelease* may also be ``None``, meaning that a planned feature got
dropped. dropped.
Instances of class _Feature have two corresponding methods, Instances of ``class _Feature`` have two corresponding methods,
.getOptionalRelease() and .getMandatoryRelease(). ``.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 will work in all releases at or after 2.1b1, and that nested_scopes are
intended to be enforced starting in release 2.2. intended to be enforced starting in release 2.2.
Resolved Problem: Runtime Compilation 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. 1. The ``exec`` statement.
2. The execfile() function. 2. The ``execfile()`` function.
3. The compile() function. 3. The ``compile()`` function.
4. The eval() function. 4. The ``eval()`` function.
5. The input() function. 5. The ``input()`` function.
Since a module M containing a future_statement naming feature F Since a module M containing a future_statement naming feature F explicitly
explicitly requests that the current release act like a future release requests that the current release act like a future release with respect to F,
with respect to F, any code compiled dynamically from text passed to any code compiled dynamically from text passed to one of these from within M
one of these from within M should probably also use the new syntax or should probably also use the new syntax or semantics associated with F. The
semantics associated with F. The 2.1 release does behave this way. 2.1 release does behave this way.
This isn't always desired, though. For example, doctest.testmod(M) This isn't always desired, though. For example, ``doctest.testmod(M)``
compiles examples taken from strings in M, and those examples should compiles examples taken from strings in M, and those examples should use M's
use M's choices, not necessarily the doctest module's choices. In the choices, not necessarily the doctest module's choices. In the 2.1 release,
2.1 release, this isn't possible, and no scheme has yet been suggested this isn't possible, and no scheme has yet been suggested for working around
for working around this. NOTE: PEP 264 later addressed this in a this. NOTE: PEP 264 later addressed this in a flexible way, by adding
flexible way, by adding optional arguments to compile(). optional arguments to ``compile()``.
In any case, a future_statement appearing "near the top" (see Syntax In any case, a future_statement appearing "near the top" (see Syntax above) of
above) of text compiled dynamically by an exec, execfile() or compile() text compiled dynamically by an ``exec``, ``execfile()`` or ``compile()``
applies to the code block generated, but has no further effect on the applies to the code block generated, but has no further effect on the module
module that executes such an exec, execfile() or compile(). This that executes such an ``exec``, ``execfile()`` or ``compile()``. This can't
can't be used to affect eval() or input(), however, because they only be used to affect ``eval()`` or ``input()``, however, because they only allow
allow expression input, and a future_statement is not an expression. expression input, and a future_statement is not an expression.
Resolved Problem: Native Interactive Shells 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 2. By invoking Python from a command line with the ``-i`` switch and with a
script argument. script argument.
An interactive shell can be seen as an extreme case of runtime An interactive shell can be seen as an extreme case of runtime compilation
compilation (see above): in effect, each statement typed at an (see above): in effect, each statement typed at an interactive shell prompt
interactive shell prompt runs a new instance of exec, compile() or runs a new instance of ``exec``, ``compile()`` or ``execfile()``. A
execfile(). A future_statement typed at an interactive shell applies to future_statement typed at an interactive shell applies to the rest of the
the rest of the shell session's life, as if the future_statement had shell session's life, as if the future_statement had appeared at the top of a
appeared at the top of a module. module.
Resolved Problem: Simulated Interactive Shells Resolved Problem: Simulated Interactive Shells
===============================================
Interactive shells "built by hand" (by tools such as IDLE and the Emacs Interactive shells "built by hand" (by tools such as IDLE and the Emacs
Python-mode) should behave like native interactive shells (see above). Python-mode) should behave like native interactive shells (see above).
However, the machinery used internally by native interactive shells has However, the machinery used internally by native interactive shells has not
not been exposed, and there isn't a clear way for tools building their been exposed, and there isn't a clear way for tools building their own
own interactive shells to achieve the desired behavior. interactive shells to achieve the desired behavior.
NOTE: PEP 264 later addressed this, by adding intelligence to the NOTE: PEP 264 later addressed this, by adding intelligence to the standard
standard codeop.py. Simulated shells that don't use the standard ``codeop.py``. Simulated shells that don't use the standard library shell
library shell helpers can get a similar effect by exploiting the helpers can get a similar effect by exploiting the new optional arguments to
new optional arguments to compile() added by PEP 264. ``compile()`` added by PEP 264.
Questions and Answers 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, Outside the scope of this PEP. Seems unlikely to the author, though. Write a
though. Write a PEP if you want to pursue it. PEP if you want to pursue it.
Q: What about incompatibilities due to changes in the Python virtual What about incompatibilities due to changes in the Python virtual machine?
machine? --------------------------------------------------------------------------
A: Outside the scope of this PEP, although PEP 5 [1] suggests a grace Outside the scope of this PEP, although PEP 5 [1]_ suggests a grace period
period there too, and the future_statement may also have a role to there too, and the future_statement may also have a role to play there.
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 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?
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 Sorry! ``try/except`` is a runtime feature; future_statements are primarily
primarily compile-time gimmicks, and your try/except happens long compile-time gimmicks, and your ``try/except`` happens long after the compiler
after the compiler is done. That is, by the time you do is done. That is, by the time you do ``try/except``, the semantics in effect
try/except, the semantics in effect for the module are already a for the module are already a done deal. Since the ``try/except`` wouldn't
done deal. Since the try/except wouldn't accomplish what it accomplish what it *looks* like it should accomplish, it's simply not allowed.
*looks* like it should accomplish, it's simply not allowed. We We also want to keep these special statements very easy to find and to
also want to keep these special statements very easy to find and to recognize.
recognize.
Note that you *can* import __future__ directly, and use the Note that you *can* import ``__future__`` directly, and use the information in
information in it, along with sys.version_info, to figure out where it, along with ``sys.version_info``, to figure out where the release you're
the release you're running under stands in relation to a given running under stands in relation to a given feature's status.
feature's status.
Q: Going back to the nested_scopes example, what if release 2.2 comes 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?
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 By continuing to use 2.1, and not moving to 2.2 until you do change your
your code. The purpose of future_statement is to make life easier code. The purpose of future_statement is to make life easier for people who
for people who keep current with the latest release in a timely keep current with the latest release in a timely fashion. We don't hate you
fashion. We don't hate you if you don't, but your problems are if you don't, but your problems are much harder to solve, and somebody with
much harder to solve, and somebody with those problems will need to those problems will need to write a PEP addressing them. future_statement is
write a PEP addressing them. future_statement is aimed at a aimed at a different audience.
different audience.
Q: Overloading "import" sucks. Why not introduce a new statement for Overloading ``import`` sucks. Why not introduce a new statement for this?
this? --------------------------------------------------------------------------
A: Like maybe "lambda lambda nested_scopes"? That is, unless we Like maybe ``lambda lambda nested_scopes``? That is, unless we introduce a
introduce a new keyword, we can't introduce an entirely new new keyword, we can't introduce an entirely new statement. But if we
statement. But if we introduce a new keyword, that in itself introduce a new keyword, that in itself would break old code. That would be
would break old code. That would be too ironic to bear. Yes, too ironic to bear. Yes, overloading ``import`` does suck, but not as
overloading "import" does suck, but not as energetically as the energetically as the alternatives -- as is, future_statements are 100%
alternatives -- as is, future_statements are 100% backward backward compatible.
compatible.
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
References and Footnotes References and Footnotes
========================
[1] PEP 5, Guidelines for Language Evolution, Prescod .. [1] PEP 5, Guidelines for Language Evolution, Prescod
http://www.python.org/dev/peps/pep-0005/ http://www.python.org/dev/peps/pep-0005/
[2] PEP 227, Statically Nested Scopes, Hylton .. [2] PEP 227, Statically Nested Scopes, Hylton
http://www.python.org/dev/peps/pep-0227/ http://www.python.org/dev/peps/pep-0227/
[3] PEP 230, Warning Framework, Van Rossum .. [3] PEP 230, Warning Framework, Van Rossum
http://www.python.org/dev/peps/pep-0230/ http://www.python.org/dev/peps/pep-0230/
[4] Note that this is "may" and not "will": better safe than sorry. Of .. [4] Note that this is *may* and not *will*: better safe than sorry. Of course
course spurious warnings won't be generated when avoidable with spurious warnings won't be generated when avoidable with reasonable cost.
reasonable cost.
[5] This ensures that a future_statement run under a release prior to .. [5] This ensures that a future_statement run under a release prior to the
the first one in which a given feature is known (but >= 2.1) will first one in which a given feature is known (but >= 2.1) will raise a
raise a compile-time error rather than silently do a wrong thing. compile-time error rather than silently do a wrong thing. If transported
If transported to a release prior to 2.1, a runtime error will be to a release prior to 2.1, a runtime error will be raised because of the
raised because of the failure to import __future__ (no such module failure to import ``__future__`` (no such module existed in the standard
existed in the standard distribution before the 2.1 release, and distribution before the 2.1 release, and the double underscores make it a
the double underscores make it a reserved name). reserved name).
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
End: indent-tabs-mode: nil
End:

View File

@ -5,169 +5,169 @@ Last-Modified: $Date$
Author: A.M. Kuchling <amk@amk.ca> Author: A.M. Kuchling <amk@amk.ca>
Status: Final Status: Final
Type: Informational Type: Informational
Content-Type: text/x-rst
Created: 23-Mar-2001 Created: 23-Mar-2001
Post-History: 20-Sep-2001 Post-History: 20-Sep-2001
Abstract
There are several different modules available that implement Abstract
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. 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 Specification
=============
All hashing modules should present the same interface. Additional All hashing modules should present the same interface. Additional methods or
methods or variables can be added, but those described in this variables can be added, but those described in this document should always be
document should always be present. present.
Hash function modules define one function: Hash function modules define one function:
new([string]) (unkeyed hashes) | ``new([string]) (unkeyed hashes)``
new([key] , [string]) (keyed hashes) | ``new([key] , [string]) (keyed hashes)``
Create a new hashing object and return it. The first form is Create a new hashing object and return it. The first form is for hashes
for hashes that are unkeyed, such as MD5 or SHA. For keyed that are unkeyed, such as MD5 or SHA. For keyed hashes such as HMAC, *key*
hashes such as HMAC, 'key' is a required parameter containing is a required parameter containing a string giving the key to use. In both
a string giving the key to use. In both cases, the optional cases, the optional *string* parameter, if supplied, will be immediately
'string' parameter, if supplied, will be immediately hashed hashed into the object's starting state, as if ``obj.update(string)``
into the object's starting state, as if obj.update(string) was was called.
called.
After creating a hashing object, arbitrary strings can be fed After creating a hashing object, arbitrary strings can be fed into the
into the object using its update() method, and the hash value object using its ``update()`` method, and the hash value can be obtained at
can be obtained at any time by calling the object's digest() any time by calling the object's ``digest()`` method.
method.
Arbitrary additional keyword arguments can be added to this Arbitrary additional keyword arguments can be added to this function, but if
function, but if they're not supplied, sensible default values they're not supplied, sensible default values should be used. For example,
should be used. For example, 'rounds' and 'digest_size' ``rounds`` and ``digest_size`` keywords could be added for a hash function
keywords could be added for a hash function which supports a which supports a variable number of rounds and several different output
variable number of rounds and several different output sizes, sizes, and they should default to values believed to be secure.
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 An integer value; the size of the digest produced by the hashing objects
hashing objects created by this module, measured in bytes. created by this module, measured in bytes. You could also obtain this value
You could also obtain this value by creating a sample object by creating a sample object and accessing its ``digest_size`` attribute, but
and accessing its 'digest_size' attribute, but it can be it can be convenient to have this value available from the module. Hashes
convenient to have this value available from the module. with a variable output size will set this variable to ``None``.
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 This attribute is identical to the module-level ``digest_size`` variable,
variable, measuring the size of the digest produced by the measuring the size of the digest produced by the hashing object, measured in
hashing object, measured in bytes. If the hash has a variable bytes. If the hash has a variable output size, this output size must be
output size, this output size must be chosen when the hashing chosen when the hashing object is created, and this attribute must contain
object is created, and this attribute must contain the the selected size. Therefore, ``None`` is *not* a legal value for this
selected size. Therefore, None is *not* a legal value for this attribute.
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 Return a separate copy of this hashing object. An update to this copy won't
this copy won't affect the original object. affect the original object.
digest() | ``digest()``
Return the hash value of this hashing object as a string Return the hash value of this hashing object as a string containing 8-bit
containing 8-bit data. The object is not altered in any way data. The object is not altered in any way by this function; you can
by this function; you can continue updating the object after continue updating the object after calling this function.
calling this function.
hexdigest() | ``hexdigest()``
Return the hash value of this hashing object as a string Return the hash value of this hashing object as a string containing
containing hexadecimal digits. Lowercase letters should be used hexadecimal digits. Lowercase letters should be used for the digits ``a``
for the digits 'a' through 'f'. Like the .digest() method, this through ``f``. Like the ``.digest()`` method, this method mustn't alter the
method mustn't alter the object. object.
update(string) | ``update(string)``
Hash 'string' into the current state of the hashing object. Hash *string* into the current state of the hashing object. ``update()`` can
update() can be called any number of times during a hashing be called any number of times during a hashing object's lifetime.
object's lifetime.
Hashing modules can define additional module-level functions or Hashing modules can define additional module-level functions or object methods
object methods and still be compliant with this specification. 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 >>> from Crypto.Hash import MD5
>>> m = MD5.new() >>> m = MD5.new()
>>> m.digest_size >>> m.digest_size
16 16
>>> m.update('abc') >>> m.update('abc')
>>> m.digest() >>> m.digest()
'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr' '\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr'
>>> m.hexdigest() >>> m.hexdigest()
'900150983cd24fb0d6963f7d28e17f72' '900150983cd24fb0d6963f7d28e17f72'
>>> MD5.new('abc').digest() >>> MD5.new('abc').digest()
'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr' '\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr'
Rationale Rationale
=========
The digest size is measured in bytes, not bits, even though hash The digest size is measured in bytes, not bits, even though hash algorithm
algorithm sizes are usually quoted in bits; MD5 is a 128-bit sizes are usually quoted in bits; MD5 is a 128-bit algorithm and not a 16-byte
algorithm and not a 16-byte one, for example. This is because, in one, for example. This is because, in the sample code I looked at, the length
the sample code I looked at, the length in bytes is often needed in bytes is often needed (to seek ahead or behind in a file; to compute the
(to seek ahead or behind in a file; to compute the length of an length of an output string) while the length in bits is rarely used. Therefore,
output string) while the length in bits is rarely used. the burden will fall on the few people actually needing the size in bits, who
Therefore, the burden will fall on the few people actually needing will have to multiply ``digest_size`` by 8.
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 It's been suggested that the ``update()`` method would be better named
append(). However, that method is really causing the current ``append()``. However, that method is really causing the current state of the
state of the hashing object to be updated, and update() is already hashing object to be updated, and ``update()`` is already used by the md5 and
used by the md5 and sha modules included with Python, so it seems sha modules included with Python, so it seems simplest to leave the name
simplest to leave the name update() alone. ``update()`` alone.
The order of the constructor's arguments for keyed hashes was a The order of the constructor's arguments for keyed hashes was a sticky issue.
sticky issue. It wasn't clear whether the key should come first It wasn't clear whether the *key* should come first or second. It's a required
or second. It's a required parameter, and the usual convention is parameter, and the usual convention is to place required parameters first, but
to place required parameters first, but that also means that the that also means that the *string* parameter moves from the first position to
'string' parameter moves from the first position to the second. the second. It would be possible to get confused and pass a single argument to
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
a keyed hash, thinking that you're passing an initial string to an hash, but it doesn't seem worth making the interface for keyed hashes more
unkeyed hash, but it doesn't seem worth making the interface obscure to avoid this potential error.
for keyed hashes more obscure to avoid this potential error.
Changes Changes
=======
2001-09-17: Renamed clear() to reset(); added digest_size attribute 2001-09-17: Renamed ``clear()`` to ``reset()``; added ``digest_size`` attribute
to objects; added .hexdigest() method. 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-20: Removed ``reset()`` method completely.
2001-09-28: Set ``digest_size`` to ``None`` for variable-size hashes.
Acknowledgements Acknowledgements
================
Thanks to Aahz, Andrew Archibald, Rich Salz, Itamar Thanks to Aahz, Andrew Archibald, Rich Salz, Itamar Shtull-Trauring, and the
Shtull-Trauring, and the readers of the python-crypto list for readers of the python-crypto list for their comments on this PEP.
their comments on this PEP.
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
End: indent-tabs-mode: nil
End:

File diff suppressed because it is too large Load Diff

View File

@ -5,541 +5,560 @@ Last-Modified: $Date$
Author: Ka-Ping Yee Author: Ka-Ping Yee
Status: Superseded Status: Superseded
Type: Standards Track Type: Standards Track
Content-Type: text/plain Content-Type: text/x-rst
Created: 12-May-2005 Created: 12-May-2005
Python-Version: 2.5 Python-Version: 2.5
Post-History: Post-History:
Numbering Note Numbering Note
==============
This PEP has been renumbered to PEP 3134. The text below is the This PEP has been renumbered to PEP 3134. The text below is the last version
last version submitted under the old number. submitted under the old number.
Abstract Abstract
========
This PEP proposes three standard attributes on exception instances: This PEP proposes three standard attributes on exception instances: the
the '__context__' attribute for implicitly chained exceptions, the ``__context__`` attribute for implicitly chained exceptions, the
'__cause__' attribute for explicitly chained exceptions, and the ``__cause__`` attribute for explicitly chained exceptions, and the
'__traceback__' attribute for the traceback. A new "raise ... from" ``__traceback__`` attribute for the traceback. A new ``raise ... from``
statement sets the '__cause__' attribute. statement sets the ``__cause__`` attribute.
Motivation Motivation
==========
During the handling of one exception (exception A), it is possible During the handling of one exception (exception A), it is possible that another
that another exception (exception B) may occur. In today's Python exception (exception B) may occur. In today's Python (version 2.4), if this
(version 2.4), if this happens, exception B is propagated outward happens, exception B is propagated outward and exception A is lost. In order
and exception A is lost. In order to debug the problem, it is to debug the problem, it is useful to know about both exceptions. The
useful to know about both exceptions. The '__context__' attribute ``__context__`` attribute retains this information automatically.
retains this information automatically.
Sometimes it can be useful for an exception handler to intentionally Sometimes it can be useful for an exception handler to intentionally re-raise
re-raise an exception, either to provide extra information or to an exception, either to provide extra information or to translate an exception
translate an exception to another type. The '__cause__' attribute to another type. The ``__cause__`` attribute provides an explicit way to
provides an explicit way to record the direct cause of an exception. record the direct cause of an exception.
In today's Python implementation, exceptions are composed of three In today's Python implementation, exceptions are composed of three parts: the
parts: the type, the value, and the traceback. The 'sys' module, type, the value, and the traceback. The ``sys`` module, exposes the current
exposes the current exception in three parallel variables, exc_type, exception in three parallel variables, ``exc_type``, ``exc_value``, and
exc_value, and exc_traceback, the sys.exc_info() function returns a ``exc_traceback``, the ``sys.exc_info()`` function returns a tuple of these
tuple of these three parts, and the 'raise' statement has a three parts, and the ``raise`` statement has a three-argument form accepting
three-argument form accepting these three parts. Manipulating these three parts. Manipulating exceptions often requires passing these three
exceptions often requires passing these three things in parallel, things in parallel, which can be tedious and error-prone. Additionally, the
which can be tedious and error-prone. Additionally, the 'except' ``except`` statement can only provide access to the value, not the traceback.
statement can only provide access to the value, not the traceback. Adding the ``__traceback__`` attribute to exception values makes all the
Adding the '__traceback__' attribute to exception values makes all exception information accessible from a single place.
the exception information accessible from a single place.
History History
=======
Raymond Hettinger [1] raised the issue of masked exceptions on Raymond Hettinger [1]_ raised the issue of masked exceptions on Python-Dev in
Python-Dev in January 2003 and proposed a PyErr_FormatAppend() January 2003 and proposed a ``PyErr_FormatAppend()`` function that C modules
function that C modules could use to augment the currently active could use to augment the currently active exception with more information.
exception with more information. Brett Cannon [2] brought up Brett Cannon [2]_ brought up chained exceptions again in June 2003, prompting
chained exceptions again in June 2003, prompting a long discussion. a long discussion.
Greg Ewing [3] identified the case of an exception occurring in a Greg Ewing [3]_ identified the case of an exception occurring in a ``finally``
'finally' block during unwinding triggered by an original exception, block during unwinding triggered by an original exception, as distinct from
as distinct from the case of an exception occurring in an 'except' the case of an exception occurring in an ``except`` block that is handling the
block that is handling the original exception. original exception.
Greg Ewing [4] and Guido van Rossum [5], and probably others, have Greg Ewing [4]_ and Guido van Rossum [5]_, and probably others, have
previously mentioned adding a traceback attribute to Exception previously mentioned adding a traceback attribute to ``Exception`` instances.
instances. This is noted in PEP 3000. This is noted in PEP 3000.
This PEP was motivated by yet another recent Python-Dev reposting This PEP was motivated by yet another recent Python-Dev reposting of the same
of the same ideas [6] [7]. ideas [6]_ [7]_.
Rationale Rationale
=========
The Python-Dev discussions revealed interest in exception chaining The Python-Dev discussions revealed interest in exception chaining for two
for two quite different purposes. To handle the unexpected raising quite different purposes. To handle the unexpected raising of a secondary
of a secondary exception, the exception must be retained implicitly. exception, the exception must be retained implicitly. To support intentional
To support intentional translation of an exception, there must be a translation of an exception, there must be a way to chain exceptions
way to chain exceptions explicitly. This PEP addresses both. explicitly. This PEP addresses both.
Several attribute names for chained exceptions have been suggested Several attribute names for chained exceptions have been suggested on Python-
on Python-Dev [2], including 'cause', 'antecedent', 'reason', Dev [2]_, including ``cause``, ``antecedent``, ``reason``, ``original``,
'original', 'chain', 'chainedexc', 'exc_chain', 'excprev', ``chain``, ``chainedexc``, ``xc_chain``, ``excprev``, ``previous`` and
'previous', and 'precursor'. For an explicitly chained exception, ``precursor``. For an explicitly chained exception, this PEP suggests
this PEP suggests '__cause__' because of its specific meaning. For ``__cause__`` because of its specific meaning. For an implicitly chained
an implicitly chained exception, this PEP proposes the name exception, this PEP proposes the name ``__context__`` because the intended
'__context__' because the intended meaning is more specific than meaning is more specific than temporal precedence but less specific than
temporal precedence but less specific than causation: an exception causation: an exception occurs in the context of handling another exception.
occurs in the context of handling another exception.
This PEP suggests names with leading and trailing double-underscores This PEP suggests names with leading and trailing double-underscores for these
for these three attributes because they are set by the Python VM. three attributes because they are set by the Python VM. Only in very special
Only in very special cases should they be set by normal assignment. cases should they be set by normal assignment.
This PEP handles exceptions that occur during 'except' blocks and This PEP handles exceptions that occur during ``except`` blocks and
'finally' blocks in the same way. Reading the traceback makes it ``finally`` blocks in the same way. Reading the traceback makes it clear
clear where the exceptions occurred, so additional mechanisms for where the exceptions occurred, so additional mechanisms for distinguishing
distinguishing the two cases would only add unnecessary complexity. the two cases would only add unnecessary complexity.
This PEP proposes that the outermost exception object (the one This PEP proposes that the outermost exception object (the one exposed for
exposed for matching by 'except' clauses) be the most recently matching by ``except`` clauses) be the most recently raised exception for
raised exception for compatibility with current behaviour. compatibility with current behaviour.
This PEP proposes that tracebacks display the outermost exception This PEP proposes that tracebacks display the outermost exception last,
last, because this would be consistent with the chronological order because this would be consistent with the chronological order of tracebacks
of tracebacks (from oldest to most recent frame) and because the (from oldest to most recent frame) and because the actual thrown exception is
actual thrown exception is easier to find on the last line. easier to find on the last line.
To keep things simpler, the C API calls for setting an exception To keep things simpler, the C API calls for setting an exception will not
will not automatically set the exception's '__context__'. Guido automatically set the exception's ``__context__``. Guido van Rossum has
van Rossum has expressed concerns with making such changes [8]. expressed concerns with making such changes [8]_.
As for other languages, Java and Ruby both discard the original As for other languages, Java and Ruby both discard the original exception when
exception when another exception occurs in a 'catch'/'rescue' or another exception occurs in a ``catch/rescue`` or ``finally/ensure`` clause.
'finally'/'ensure' clause. Perl 5 lacks built-in structured Perl 5 lacks built-in structured exception handling. For Perl 6, RFC number
exception handling. For Perl 6, RFC number 88 [9] proposes an exception 88 [9]_ proposes an exception mechanism that implicitly retains chained
mechanism that implicitly retains chained exceptions in an array exceptions in an array named ``@@``. In that RFC, the most recently raised
named @@. In that RFC, the most recently raised exception is exception is exposed for matching, as in this PEP; also, arbitrary expressions
exposed for matching, as in this PEP; also, arbitrary expressions (possibly involving ``@@``) can be evaluated for exception matching.
(possibly involving @@) can be evaluated for exception matching.
Exceptions in C# contain a read-only 'InnerException' property that Exceptions in C# contain a read-only ``InnerException`` property that may
may point to another exception. Its documentation [10] says that point to another exception. Its documentation [10]_ says that "When an
"When an exception X is thrown as a direct result of a previous exception X is thrown as a direct result of a previous exception Y, the
exception Y, the InnerException property of X should contain a ``InnerException`` property of X should contain a reference to Y." This
reference to Y." This property is not set by the VM automatically; property is not set by the VM automatically; rather, all exception
rather, all exception constructors take an optional 'innerException' constructors take an optional ``innerException`` argument to set it
argument to set it explicitly. The '__cause__' attribute fulfills explicitly. The ``__cause__`` attribute fulfills the same purpose as
the same purpose as InnerException, but this PEP proposes a new form ``InnerException``, but this PEP proposes a new form of ``raise`` rather than
of 'raise' rather than extending the constructors of all exceptions. extending the constructors of all exceptions. C# also provides a
C# also provides a GetBaseException method that jumps directly to ``GetBaseException`` method that jumps directly to the end of the
the end of the InnerException chain; this PEP proposes no analog. ``InnerException`` chain; this PEP proposes no analog.
The reason all three of these attributes are presented together in The reason all three of these attributes are presented together in one proposal
one proposal is that the '__traceback__' attribute provides is that the ``__traceback__`` attribute provides convenient access to the
convenient access to the traceback on chained exceptions. traceback on chained exceptions.
Implicit Exception Chaining 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: try:
a/b compute()
except Exception, exc: except Exception, exc:
log(exc) log(file, 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)
finally: finally:
file.clos() # oops, misspelled 'close' file.clos() # oops, misspelled 'close'
def compute(): def compute():
1/0 1/0
def log(file, exc): def log(file, exc):
try: try:
print >>file, exc # oops, file is not writable print >>file, exc # oops, file is not writable
except: except:
display(exc) display(exc)
def display(exc): def display(exc):
print ex # oops, misspelled 'exc' print ex # oops, misspelled 'exc'
Calling main() with the name of an existing file will trigger four Calling ``main()`` with the name of an existing file will trigger four
exceptions. The ultimate result will be an AttributeError due to exceptions. The ultimate result will be an ``AttributeError`` due to the
the misspelling of 'clos', whose __context__ points to a NameError misspelling of ``clos``, whose ``__context__`` points to a ``NameError`` due
due to the misspelling of 'ex', whose __context__ points to an to the misspelling of ``ex``, whose ``__context__`` points to an ``IOError``
IOError due to the file being read-only, whose __context__ points to due to the file being read-only, whose ``__context__`` points to a
a ZeroDivisionError, whose __context__ attribute is None. ``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 2. Whenever an exception is raised, if the exception instance does not
not already have a '__context__' attribute, the interpreter sets already have a ``__context__`` attribute, the interpreter sets it equal to
it equal to the thread's exception context. the thread's exception context.
3. Immediately after an exception is raised, the thread's exception 3. Immediately after an exception is raised, the thread's exception context is
context is set to the exception. set to the exception.
4. Whenever the interpreter exits an 'except' block by reaching the 4. Whenever the interpreter exits an ``except`` block by reaching the end or
end or executing a 'return', 'yield', 'continue', or 'break' executing a ``return``, ``yield``, ``continue``, or ``break`` statement,
statement, the thread's exception context is set to None. the thread's exception context is set to ``None``.
Explicit Exception Chaining Explicit Exception Chaining
===========================
The '__cause__' attribute on exception objects is always initialized The ``__cause__`` attribute on exception objects is always initialized to
to None. It is set by a new form of the 'raise' statement: ``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 = EXCEPTION
exc.__cause__ = CAUSE exc.__cause__ = CAUSE
raise exc raise exc
In the following example, a database provides implementations for a In the following example, a database provides implementations for a few
few different kinds of storage, with file storage as one kind. The different kinds of storage, with file storage as one kind. The database
database designer wants errors to propagate as DatabaseError objects designer wants errors to propagate as ``DatabaseError`` objects so that the
so that the client doesn't have to be aware of the storage-specific client doesn't have to be aware of the storage-specific details, but doesn't
details, but doesn't want to lose the underlying error information. want to lose the underlying error information::
class DatabaseError(StandardError): class DatabaseError(StandardError):
pass pass
class FileDatabase(Database): class FileDatabase(Database):
def __init__(self, filename): def __init__(self, filename):
try: try:
self.file = open(filename) self.file = open(filename)
except IOError, exc: except IOError, exc:
raise DatabaseError('failed to open') from exc raise DatabaseError('failed to open') from exc
If the call to open() raises an exception, the problem will be If the call to ``open()`` raises an exception, the problem will be reported as
reported as a DatabaseError, with a __cause__ attribute that reveals a ``DatabaseError``, with a ``__cause__`` attribute that reveals the
the IOError as the original cause. ``IOError`` as the original cause.
Traceback Attribute Traceback Attribute
===================
The following example illustrates the '__traceback__' attribute. The following example illustrates the ``__traceback__`` attribute::
def do_logged(file, work): def do_logged(file, work):
try: try:
work() work()
except Exception, exc: except Exception, exc:
write_exception(file, exc) write_exception(file, exc)
raise exc raise exc
from traceback import format_tb from traceback import format_tb
def write_exception(file, exc): def write_exception(file, exc):
... ...
type = exc.__class__ type = exc.__class__
message = str(exc) message = str(exc)
lines = format_tb(exc.__traceback__) lines = format_tb(exc.__traceback__)
file.write(... type ... message ... lines ...) file.write(... type ... message ... lines ...)
... ...
In today's Python, the do_logged() function would have to extract In today's Python, the ``do_logged()`` function would have to extract the
the traceback from sys.exc_traceback or sys.exc_info()[2] and pass traceback from ``sys.exc_traceback`` or ``sys.exc_info()`` [2]_ and pass both
both the value and the traceback to write_exception(). With the the value and the traceback to ``write_exception()``. With the proposed
proposed change, write_exception() simply gets one argument and change, ``write_exception()`` simply gets one argument and obtains the
obtains the exception using the '__traceback__' attribute. 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 1. Whenever an exception is caught, if the exception instance does not already
not already have a '__traceback__' attribute, the interpreter have a ``__traceback__`` attribute, the interpreter sets it to the newly
sets it to the newly caught traceback. caught traceback.
Enhanced Reporting Enhanced Reporting
==================
The default exception handler will be modified to report chained The default exception handler will be modified to report chained exceptions.
exceptions. The chain of exceptions is traversed by following the The chain of exceptions is traversed by following the ``__cause__`` and
'__cause__' and '__context__' attributes, with '__cause__' taking ``__context__`` attributes, with ``__cause__`` taking priority. In keeping
priority. In keeping with the chronological order of tracebacks, with the chronological order of tracebacks, the most recently raised exception
the most recently raised exception is displayed last; that is, the is displayed last; that is, the display begins with the description of the
display begins with the description of the innermost exception and innermost exception and backs up the chain to the outermost exception. The
backs up the chain to the outermost exception. The tracebacks are tracebacks are formatted as usual, with one of the lines::
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__ During handling of the above exception, another exception occurred:
or __context__ respectively. Here is a sketch of the procedure:
def print_chain(exc): between tracebacks, depending whether they are linked by ``__cause__`` or
if exc.__cause__: ``__context__`` respectively. Here is a sketch of the procedure::
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)
In the 'traceback' module, the format_exception, print_exception, def print_chain(exc):
print_exc, and print_last functions will be updated to accept an if exc.__cause__:
optional 'chain' argument, True by default. When this argument is print_chain(exc.__cause__)
True, these functions will format or display the entire chain of print '\nThe above exception was the direct cause...'
exceptions as just described. When it is False, these functions elif exc.__context__:
will format or display only the outermost exception. 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 In the ``traceback`` module, the ``format_exception``, ``print_exception``,
chain of exceptions. ``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 C API
=====
The PyErr_Set* calls for setting exceptions will not set the The ``PyErr_Set*`` calls for setting exceptions will not set the
'__context__' attribute on exceptions. PyErr_NormalizeException ``__context__`` attribute on exceptions. ``PyErr_NormalizeException`` will
will always set the 'traceback' attribute to its 'tb' argument and always set the ``traceback`` attribute to its ``tb`` argument and the
the '__context__' and '__cause__' attributes to None. ``__context__`` and ``__cause__`` attributes to ``None``.
A new API function, PyErr_SetContext(context), will help C A new API function, ``PyErr_SetContext(context)``, will help C programmers
programmers provide chained exception information. This function provide chained exception information. This function will first normalize the
will first normalize the current exception so it is an instance, current exception so it is an instance, then set its ``__context__``
then set its '__context__' attribute. A similar API function, attribute. A similar API function, ``PyErr_SetCause(cause)``, will set the
PyErr_SetCause(cause), will set the '__cause__' attribute. ``__cause__`` attribute.
Compatibility Compatibility
=============
Chained exceptions expose the type of the most recent exception, so Chained exceptions expose the type of the most recent exception, so they will
they will still match the same 'except' clauses as they do now. still match the same ``except`` clauses as they do now.
The proposed changes should not break any code unless it sets or The proposed changes should not break any code unless it sets or uses
uses attributes named '__context__', '__cause__', or '__traceback__' attributes named ``__context__``, ``__cause__``, or ``__traceback__`` on
on exception instances. As of 2005-05-12, the Python standard exception instances. As of 2005-05-12, the Python standard library contains
library contains no mention of such attributes. no mention of such attributes.
Open Issue: Extra Information Open Issue: Extra Information
==============================
Walter Dörwald [11] expressed a desire to attach extra information Walter Dörwald [11]_ expressed a desire to attach extra information to an
to an exception during its upward propagation without changing its exception during its upward propagation without changing its type. This could
type. This could be a useful feature, but it is not addressed by be a useful feature, but it is not addressed by this PEP. It could
this PEP. It could conceivably be addressed by a separate PEP conceivably be addressed by a separate PEP establishing conventions for other
establishing conventions for other informational attributes on informational attributes on exceptions.
exceptions.
Open Issue: Suppressing Context Open Issue: Suppressing Context
================================
As written, this PEP makes it impossible to suppress '__context__', As written, this PEP makes it impossible to suppress ``__context__``, since
since setting exc.__context__ to None in an 'except' or 'finally' setting ``exc.__context__`` to ``None`` in an ``except`` or ``finally`` clause
clause will only result in it being set again when exc is raised. will only result in it being set again when ``exc`` is raised.
Open Issue: Limiting Exception Types Open Issue: Limiting Exception Types
=====================================
To improve encapsulation, library implementors may want to wrap all To improve encapsulation, library implementors may want to wrap all
implementation-level exceptions with an application-level exception. implementation-level exceptions with an application-level exception. One could
One could try to wrap exceptions by writing this: try to wrap exceptions by writing this::
try: try:
... implementation may raise an exception ... ... implementation may raise an exception ...
except: except:
import sys import sys
raise ApplicationError from sys.exc_value 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 try:
the current exception in a catch-all 'except' clause, but that isn't ... implementation may raise an exception ...
addressed here. Such a feature would allow something like this: except Exception, exc:
raise ApplicationError from exc
try: but both are somewhat flawed. It would be nice to be able to name the current
... implementation may raise an exception ... exception in a catch-all ``except`` clause, but that isn't addressed here.
except *, exc: Such a feature would allow something like this::
raise ApplicationError from exc
try:
... implementation may raise an exception ...
except *, exc:
raise ApplicationError from exc
Open Issue: yield Open Issue: yield
==================
The exception context is lost when a 'yield' statement is executed; The exception context is lost when a ``yield`` statement is executed; resuming
resuming the frame after the 'yield' does not restore the context. the frame after the ``yield`` does not restore the context. Addressing this
Addressing this problem is out of the scope of this PEP; it is not a problem is out of the scope of this PEP; it is not a new problem, as
new problem, as demonstrated by the following example: demonstrated by the following example::
>>> def gen(): >>> def gen():
... try: ... try:
... 1/0 ... 1/0
... except: ... except:
... yield 3 ... yield 3
... raise ... raise
... ...
>>> g = gen() >>> g = gen()
>>> g.next() >>> g.next()
3 3
>>> g.next() >>> g.next()
TypeError: exceptions must be classes, instances, or strings TypeError: exceptions must be classes, instances, or strings
(deprecated), not NoneType (deprecated), not NoneType
Open Issue: Garbage Collection Open Issue: Garbage Collection
===============================
The strongest objection to this proposal has been that it creates The strongest objection to this proposal has been that it creates cycles
cycles between exceptions and stack frames [12]. Collection of between exceptions and stack frames [12]_. Collection of cyclic garbage (and
cyclic garbage (and therefore resource release) can be greatly therefore resource release) can be greatly delayed::
delayed.
>>> try: >>> try:
>>> 1/0 >>> 1/0
>>> except Exception, err: >>> except Exception, err:
>>> pass >>> pass
will introduce a cycle from err -> traceback -> stack frame -> err, will introduce a cycle from err -> traceback -> stack frame -> err, keeping
keeping all locals in the same scope alive until the next GC happens. 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 Today, these locals would go out of scope. There is lots of code which
which assumes that "local" resources -- particularly open files -- will assumes that "local" resources -- particularly open files -- will be closed
be closed quickly. If closure has to wait for the next GC, a program quickly. If closure has to wait for the next GC, a program (which runs fine
(which runs fine today) may run out of file handles. today) may run out of file handles.
Making the __traceback__ attribute a weak reference would avoid the Making the ``__traceback__`` attribute a weak reference would avoid the
problems with cyclic garbage. Unfortunately, it would make saving problems with cyclic garbage. Unfortunately, it would make saving the
the Exception for later (as unittest does) more awkward, and it would ``Exception`` for later (as ``unittest`` does) more awkward, and it would not
not allow as much cleanup of the sys module. allow as much cleanup of the ``sys`` module.
A possible alternate solution, suggested by Adam Olsen, would be to A possible alternate solution, suggested by Adam Olsen, would be to instead
instead turn the reference from the stack frame to the 'err' variable turn the reference from the stack frame to the ``err`` variable into a weak
into a weak reference when the variable goes out of scope [13]. reference when the variable goes out of scope [13]_.
Possible Future Compatible Changes Possible Future Compatible Changes
==================================
These changes are consistent with the appearance of exceptions as These changes are consistent with the appearance of exceptions as a single
a single object rather than a triple at the interpreter level. object rather than a triple at the interpreter level.
- If PEP 340 or PEP 343 is accepted, replace the three (type, value, - If PEP 340 or PEP 343 is accepted, replace the three (``type``, ``value``,
traceback) arguments to __exit__ with a single exception argument. ``traceback``) arguments to ``__exit__`` with a single exception argument.
- Deprecate sys.exc_type, sys.exc_value, sys.exc_traceback, and - Deprecate ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
sys.exc_info() in favour of a single member, sys.exception. ``sys.exc_info()`` in favour of a single member, ``sys.exception``.
- Deprecate sys.last_type, sys.last_value, and sys.last_traceback - Deprecate ``sys.last_type``, ``sys.last_value``, and ``sys.last_traceback``
in favour of a single member, sys.last_exception. in favour of a single member, ``sys.last_exception``.
- Deprecate the three-argument form of the 'raise' statement in - Deprecate the three-argument form of the ``raise`` statement in favour of
favour of the one-argument form. the one-argument form.
- Upgrade cgitb.html() to accept a single value as its first - Upgrade ``cgitb.html()`` to accept a single value as its first argument as
argument as an alternative to a (type, value, traceback) tuple. an alternative to a ``(type, value, traceback)`` tuple.
Possible Future Incompatible Changes 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 - Remove ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
sys.exc_info(). ``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 - Replace the three-argument ``sys.excepthook`` with a one-argument API, and
API, and changing the 'cgitb' module to match. 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' - Upgrade ``traceback.print_exception`` to accept an ``exception`` argument
argument instead of the type, value, and traceback arguments. instead of the ``type``, ``value``, and ``traceback`` arguments.
Acknowledgements Acknowledgements
================
Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip J. Eby,
J. Eby, Raymond Hettinger, Walter Dörwald, and others. Raymond Hettinger, Walter Dörwald, and others.
References References
==========
[1] Raymond Hettinger, "Idea for avoiding exception masking" .. [1] Raymond Hettinger, "Idea for avoiding exception masking"
http://mail.python.org/pipermail/python-dev/2003-January/032492.html http://mail.python.org/pipermail/python-dev/2003-January/032492.html
[2] Brett Cannon explains chained exceptions .. [2] Brett Cannon explains chained exceptions
http://mail.python.org/pipermail/python-dev/2003-June/036063.html http://mail.python.org/pipermail/python-dev/2003-June/036063.html
[3] Greg Ewing points out masking caused by exceptions during finally .. [3] Greg Ewing points out masking caused by exceptions during finally
http://mail.python.org/pipermail/python-dev/2003-June/036290.html http://mail.python.org/pipermail/python-dev/2003-June/036290.html
[4] Greg Ewing suggests storing the traceback in the exception object .. [4] Greg Ewing suggests storing the traceback in the exception object
http://mail.python.org/pipermail/python-dev/2003-June/036092.html http://mail.python.org/pipermail/python-dev/2003-June/036092.html
[5] Guido van Rossum mentions exceptions having a traceback attribute .. [5] Guido van Rossum mentions exceptions having a traceback attribute
http://mail.python.org/pipermail/python-dev/2005-April/053060.html http://mail.python.org/pipermail/python-dev/2005-April/053060.html
[6] Ka-Ping Yee, "Tidier Exceptions" .. [6] Ka-Ping Yee, "Tidier Exceptions"
http://mail.python.org/pipermail/python-dev/2005-May/053671.html http://mail.python.org/pipermail/python-dev/2005-May/053671.html
[7] Ka-Ping Yee, "Chained Exceptions" .. [7] Ka-Ping Yee, "Chained Exceptions"
http://mail.python.org/pipermail/python-dev/2005-May/053672.html http://mail.python.org/pipermail/python-dev/2005-May/053672.html
[8] Guido van Rossum discusses automatic chaining in PyErr_Set* .. [8] Guido van Rossum discusses automatic chaining in ``PyErr_Set*``
http://mail.python.org/pipermail/python-dev/2003-June/036180.html http://mail.python.org/pipermail/python-dev/2003-June/036180.html
[9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism" .. [9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
http://dev.perl.org/perl6/rfc/88.html 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 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 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 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 http://mail.python.org/pipermail/python-3000/2007-January/005363.html
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
sentence-end-double-space: t indent-tabs-mode: nil
fill-column: 70 sentence-end-double-space: t
coding: utf-8 fill-column: 70
End: coding: utf-8
End:

View File

@ -5,550 +5,574 @@ Last-Modified: $Date$
Author: Ka-Ping Yee Author: Ka-Ping Yee
Status: Final Status: Final
Type: Standards Track Type: Standards Track
Content-Type: text/plain Content-Type: text/x-rst
Created: 12-May-2005 Created: 12-May-2005
Python-Version: 3.0 Python-Version: 3.0
Post-History: Post-History:
Numbering Note Numbering Note
==============
This PEP started its life as PEP 344. Since it is now targeted This PEP started its life as PEP 344. Since it is now targeted for Python
for Python 3000, it has been moved into the 3xxx space. 3000, it has been moved into the 3xxx space.
Abstract Abstract
========
This PEP proposes three standard attributes on exception instances: This PEP proposes three standard attributes on exception instances: the
the '__context__' attribute for implicitly chained exceptions, the ``__context__`` attribute for implicitly chained exceptions, the ``__cause__``
'__cause__' attribute for explicitly chained exceptions, and the attribute for explicitly chained exceptions, and the ``__traceback__``
'__traceback__' attribute for the traceback. A new "raise ... from" attribute for the traceback. A new ``raise ... from`` statement sets the
statement sets the '__cause__' attribute. ``__cause__`` attribute.
Motivation Motivation
==========
During the handling of one exception (exception A), it is possible During the handling of one exception (exception A), it is possible that another
that another exception (exception B) may occur. In today's Python exception (exception B) may occur. In today's Python (version 2.4), if this
(version 2.4), if this happens, exception B is propagated outward happens, exception B is propagated outward and exception A is lost. In order
and exception A is lost. In order to debug the problem, it is to debug the problem, it is useful to know about both exceptions. The
useful to know about both exceptions. The '__context__' attribute ``__context__`` attribute retains this information automatically.
retains this information automatically.
Sometimes it can be useful for an exception handler to intentionally Sometimes it can be useful for an exception handler to intentionally re-raise
re-raise an exception, either to provide extra information or to an exception, either to provide extra information or to translate an exception
translate an exception to another type. The '__cause__' attribute to another type. The ``__cause__`` attribute provides an explicit way to
provides an explicit way to record the direct cause of an exception. record the direct cause of an exception.
In today's Python implementation, exceptions are composed of three In today's Python implementation, exceptions are composed of three parts: the
parts: the type, the value, and the traceback. The 'sys' module, type, the value, and the traceback. The ``sys`` module, exposes the current
exposes the current exception in three parallel variables, exc_type, exception in three parallel variables, ``exc_type``, ``exc_value``, and
exc_value, and exc_traceback, the sys.exc_info() function returns a ``exc_traceback``, the ``sys.exc_info()`` function returns a tuple of these
tuple of these three parts, and the 'raise' statement has a three parts, and the ``raise`` statement has a three-argument form accepting
three-argument form accepting these three parts. Manipulating these three parts. Manipulating exceptions often requires passing these three
exceptions often requires passing these three things in parallel, things in parallel, which can be tedious and error-prone. Additionally, the
which can be tedious and error-prone. Additionally, the 'except' ``except`` statement can only provide access to the value, not the traceback.
statement can only provide access to the value, not the traceback. Adding the ``__traceback__`` attribute to exception values makes all the
Adding the '__traceback__' attribute to exception values makes all exception information accessible from a single place.
the exception information accessible from a single place.
History History
=======
Raymond Hettinger [1] raised the issue of masked exceptions on Raymond Hettinger [1]_ raised the issue of masked exceptions on Python-Dev in
Python-Dev in January 2003 and proposed a PyErr_FormatAppend() January 2003 and proposed a ``PyErr_FormatAppend()`` function that C modules
function that C modules could use to augment the currently active could use to augment the currently active exception with more information.
exception with more information. Brett Cannon [2] brought up Brett Cannon [2]_ brought up chained exceptions again in June 2003, prompting
chained exceptions again in June 2003, prompting a long discussion. a long discussion.
Greg Ewing [3] identified the case of an exception occurring in a Greg Ewing [3]_ identified the case of an exception occurring in a ``finally``
'finally' block during unwinding triggered by an original exception, block during unwinding triggered by an original exception, as distinct from
as distinct from the case of an exception occurring in an 'except' the case of an exception occurring in an ``except`` block that is handling the
block that is handling the original exception. original exception.
Greg Ewing [4] and Guido van Rossum [5], and probably others, have Greg Ewing [4]_ and Guido van Rossum [5]_, and probably others, have
previously mentioned adding a traceback attribute to Exception previously mentioned adding a traceback attribute to Exception instances.
instances. This is noted in PEP 3000. This is noted in PEP 3000.
This PEP was motivated by yet another recent Python-Dev reposting This PEP was motivated by yet another recent Python-Dev reposting of the same
of the same ideas [6] [7]. ideas [6]_ [7]_.
Rationale Rationale
=========
The Python-Dev discussions revealed interest in exception chaining The Python-Dev discussions revealed interest in exception chaining for two
for two quite different purposes. To handle the unexpected raising quite different purposes. To handle the unexpected raising of a secondary
of a secondary exception, the exception must be retained implicitly. exception, the exception must be retained implicitly. To support intentional
To support intentional translation of an exception, there must be a translation of an exception, there must be a way to chain exceptions
way to chain exceptions explicitly. This PEP addresses both. explicitly. This PEP addresses both.
Several attribute names for chained exceptions have been suggested Several attribute names for chained exceptions have been suggested on
on Python-Dev [2], including 'cause', 'antecedent', 'reason', Python-Dev [2]_, including ``cause``, ``antecedent``, ``reason``, ``original``,
'original', 'chain', 'chainedexc', 'exc_chain', 'excprev', ``chain``, ``chainedexc``, ``exc_chain``, ``excprev``, ``previous``, and
'previous', and 'precursor'. For an explicitly chained exception, ``precursor``. For an explicitly chained exception, this PEP suggests
this PEP suggests '__cause__' because of its specific meaning. For ``__cause__`` because of its specific meaning. For an implicitly chained
an implicitly chained exception, this PEP proposes the name exception, this PEP proposes the name ``__context__`` because the intended
'__context__' because the intended meaning is more specific than meaning is more specific than temporal precedence but less specific than
temporal precedence but less specific than causation: an exception causation: an exception occurs in the context of handling another exception.
occurs in the context of handling another exception.
This PEP suggests names with leading and trailing double-underscores This PEP suggests names with leading and trailing double-underscores for these
for these three attributes because they are set by the Python VM. three attributes because they are set by the Python VM. Only in very special
Only in very special cases should they be set by normal assignment. cases should they be set by normal assignment.
This PEP handles exceptions that occur during 'except' blocks and This PEP handles exceptions that occur during ``except`` blocks and ``finally``
'finally' blocks in the same way. Reading the traceback makes it blocks in the same way. Reading the traceback makes it clear where the
clear where the exceptions occurred, so additional mechanisms for exceptions occurred, so additional mechanisms for distinguishing the two cases
distinguishing the two cases would only add unnecessary complexity. would only add unnecessary complexity.
This PEP proposes that the outermost exception object (the one This PEP proposes that the outermost exception object (the one exposed for
exposed for matching by 'except' clauses) be the most recently matching by ``except`` clauses) be the most recently raised exception for
raised exception for compatibility with current behaviour. compatibility with current behaviour.
This PEP proposes that tracebacks display the outermost exception This PEP proposes that tracebacks display the outermost exception last, because
last, because this would be consistent with the chronological order this would be consistent with the chronological order of tracebacks (from
of tracebacks (from oldest to most recent frame) and because the oldest to most recent frame) and because the actual thrown exception is easier
actual thrown exception is easier to find on the last line. to find on the last line.
To keep things simpler, the C API calls for setting an exception To keep things simpler, the C API calls for setting an exception will not
will not automatically set the exception's '__context__'. Guido automatically set the exception's ``__context__``. Guido van Rossum has
van Rossum has expressed concerns with making such changes [8]. expressed concerns with making such changes [8]_.
As for other languages, Java and Ruby both discard the original As for other languages, Java and Ruby both discard the original exception when
exception when another exception occurs in a 'catch'/'rescue' or another exception occurs in a ``catch``/``rescue`` or ``finally``/``ensure``
'finally'/'ensure' clause. Perl 5 lacks built-in structured clause. Perl 5 lacks built-in structured exception handling. For Perl 6, RFC
exception handling. For Perl 6, RFC number 88 [9] proposes an exception number 88 [9]_ proposes an exception mechanism that implicitly retains chained
mechanism that implicitly retains chained exceptions in an array exceptions in an array named ``@@``. In that RFC, the most recently raised
named @@. In that RFC, the most recently raised exception is exception is exposed for matching, as in this PEP; also, arbitrary expressions
exposed for matching, as in this PEP; also, arbitrary expressions (possibly involving ``@@``) can be evaluated for exception matching.
(possibly involving @@) can be evaluated for exception matching.
Exceptions in C# contain a read-only 'InnerException' property that Exceptions in C# contain a read-only ``InnerException`` property that may point
may point to another exception. Its documentation [10] says that to another exception. Its documentation [10]_ says that "When an exception X
"When an exception X is thrown as a direct result of a previous is thrown as a direct result of a previous exception Y, the ``InnerException``
exception Y, the InnerException property of X should contain a property of X should contain a reference to Y." This property is not set by
reference to Y." This property is not set by the VM automatically; the VM automatically; rather, all exception constructors take an optional
rather, all exception constructors take an optional 'innerException' ``innerException`` argument to set it explicitly. The ``__cause__`` attribute
argument to set it explicitly. The '__cause__' attribute fulfills fulfills the same purpose as ``InnerException``, but this PEP proposes a new
the same purpose as InnerException, but this PEP proposes a new form form of ``raise`` rather than extending the constructors of all exceptions. C#
of 'raise' rather than extending the constructors of all exceptions. also provides a ``GetBaseException`` method that jumps directly to the end of
C# also provides a GetBaseException method that jumps directly to the ``InnerException`` chain; this PEP proposes no analog.
the end of the InnerException chain; this PEP proposes no analog.
The reason all three of these attributes are presented together in The reason all three of these attributes are presented together in one proposal
one proposal is that the '__traceback__' attribute provides is that the ``__traceback__`` attribute provides convenient access to the
convenient access to the traceback on chained exceptions. traceback on chained exceptions.
Implicit Exception Chaining 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: try:
a/b a/b
except Exception, exc: except Exception, exc:
log(exc) log(exc)
def log(exc): def log(exc):
file = open('logfile.txt') # oops, forgot the 'w' file = open('logfile.txt') # oops, forgot the 'w'
print >>file, exc print >>file, exc
file.close() file.close()
Calling compute(0, 0) causes a ZeroDivisionError. The compute() Calling ``compute(0, 0)`` causes a ``ZeroDivisionError``. The ``compute()``
function catches this exception and calls log(exc), but the log() function catches this exception and calls ``log(exc)``, but the ``log()``
function also raises an exception when it tries to write to a function also raises an exception when it tries to write to a file that wasn't
file that wasn't opened for writing. opened for writing.
In today's Python, the caller of compute() gets thrown an IOError. In today's Python, the caller of ``compute()`` gets thrown an ``IOError``. The
The ZeroDivisionError is lost. With the proposed change, the ``ZeroDivisionError`` is lost. With the proposed change, the instance of
instance of IOError has an additional '__context__' attribute that ``IOError`` has an additional ``__context__`` attribute that retains the
retains the ZeroDivisionError. ``ZeroDivisionError``.
The following more elaborate example demonstrates the handling of a The following more elaborate example demonstrates the handling of a mixture of
mixture of 'finally' and 'except' clauses: ``finally`` and ``except`` clauses::
def main(filename): def main(filename):
file = open(filename) # oops, forgot the 'w' file = open(filename) # oops, forgot the 'w'
try: try:
try: try:
compute() compute()
except Exception, exc: except Exception, exc:
log(file, exc) log(file, exc)
finally: finally:
file.clos() # oops, misspelled 'close' file.clos() # oops, misspelled 'close'
def compute(): def compute():
1/0 1/0
def log(file, exc): def log(file, exc):
try: try:
print >>file, exc # oops, file is not writable print >>file, exc # oops, file is not writable
except: except:
display(exc) display(exc)
def display(exc): def display(exc):
print ex # oops, misspelled 'exc' print ex # oops, misspelled 'exc'
Calling main() with the name of an existing file will trigger four Calling ``main()`` with the name of an existing file will trigger four
exceptions. The ultimate result will be an AttributeError due to exceptions. The ultimate result will be an ``AttributeError`` due to the
the misspelling of 'clos', whose __context__ points to a NameError misspelling of ``clos``, whose ``__context__`` points to a ``NameError`` due
due to the misspelling of 'ex', whose __context__ points to an to the misspelling of ``ex``, whose ``__context__`` points to an ``IOError``
IOError due to the file being read-only, whose __context__ points to due to the file being read-only, whose ``__context__`` points to a
a ZeroDivisionError, whose __context__ attribute is None. ``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 2. Whenever an exception is raised, if the exception instance does not already
not already have a '__context__' attribute, the interpreter sets have a ``__context__`` attribute, the interpreter sets it equal to the
it equal to the thread's exception context. thread's exception context.
3. Immediately after an exception is raised, the thread's exception 3. Immediately after an exception is raised, the thread's exception context is
context is set to the exception. set to the exception.
4. Whenever the interpreter exits an 'except' block by reaching the 4. Whenever the interpreter exits an ``except`` block by reaching the end or
end or executing a 'return', 'yield', 'continue', or 'break' executing a ``return``, ``yield``, ``continue``, or ``break`` statement, the
statement, the thread's exception context is set to None. thread's exception context is set to ``None``.
Explicit Exception Chaining Explicit Exception Chaining
===========================
The '__cause__' attribute on exception objects is always initialized The ``__cause__`` attribute on exception objects is always initialized to
to None. It is set by a new form of the 'raise' statement: ``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 = EXCEPTION
exc.__cause__ = CAUSE exc.__cause__ = CAUSE
raise exc raise exc
In the following example, a database provides implementations for a In the following example, a database provides implementations for a few
few different kinds of storage, with file storage as one kind. The different kinds of storage, with file storage as one kind. The database
database designer wants errors to propagate as DatabaseError objects designer wants errors to propagate as ``DatabaseError`` objects so that the
so that the client doesn't have to be aware of the storage-specific client doesn't have to be aware of the storage-specific details, but doesn't
details, but doesn't want to lose the underlying error information. want to lose the underlying error information.
class DatabaseError(Exception): ::
pass
class FileDatabase(Database): class DatabaseError(Exception):
def __init__(self, filename): pass
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 class FileDatabase(Database):
reported as a DatabaseError, with a __cause__ attribute that reveals def __init__(self, filename):
the IOError as the original cause. 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 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): from traceback import format_tb
...
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 def write_exception(file, exc):
the traceback from sys.exc_traceback or sys.exc_info()[2] and pass ...
both the value and the traceback to write_exception(). With the type = exc.__class__
proposed change, write_exception() simply gets one argument and message = str(exc)
obtains the exception using the '__traceback__' attribute. 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 The proposed semantics are as follows:
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 Enhanced Reporting
==================
The default exception handler will be modified to report chained The default exception handler will be modified to report chained exceptions.
exceptions. The chain of exceptions is traversed by following the The chain of exceptions is traversed by following the ``__cause__`` and
'__cause__' and '__context__' attributes, with '__cause__' taking ``__context__`` attributes, with ``__cause__`` taking priority. In keeping
priority. In keeping with the chronological order of tracebacks, with the chronological order of tracebacks, the most recently raised exception
the most recently raised exception is displayed last; that is, the is displayed last; that is, the display begins with the description of the
display begins with the description of the innermost exception and innermost exception and backs up the chain to the outermost exception. The
backs up the chain to the outermost exception. The tracebacks are tracebacks are formatted as usual, with one of the lines::
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__ During handling of the above exception, another exception occurred:
or __context__ respectively. Here is a sketch of the procedure:
def print_chain(exc): between tracebacks, depending whether they are linked by ``__cause__`` or
if exc.__cause__: ``__context__`` respectively. Here is a sketch of the procedure::
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)
In the 'traceback' module, the format_exception, print_exception, def print_chain(exc):
print_exc, and print_last functions will be updated to accept an if exc.__cause__:
optional 'chain' argument, True by default. When this argument is print_chain(exc.__cause__)
True, these functions will format or display the entire chain of print '\nThe above exception was the direct cause...'
exceptions as just described. When it is False, these functions elif exc.__context__:
will format or display only the outermost exception. 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 In the ``traceback`` module, the ``format_exception``, ``print_exception``,
chain of exceptions. ``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 C API
=====
The PyErr_Set* calls for setting exceptions will not set the The ``PyErr_Set*`` calls for setting exceptions will not set the
'__context__' attribute on exceptions. PyErr_NormalizeException ``__context__`` attribute on exceptions. ``PyErr_NormalizeException`` will
will always set the 'traceback' attribute to its 'tb' argument and always set the ``traceback`` attribute to its ``tb`` argument and the
the '__context__' and '__cause__' attributes to None. ``__context__`` and ``__cause__`` attributes to ``None``.
A new API function, PyErr_SetContext(context), will help C A new API function, ``PyErr_SetContext(context)``, will help C programmers
programmers provide chained exception information. This function provide chained exception information. This function will first normalize the
will first normalize the current exception so it is an instance, current exception so it is an instance, then set its ``__context__`` attribute.
then set its '__context__' attribute. A similar API function, A similar API function, ``PyErr_SetCause(cause)``, will set the ``__cause__``
PyErr_SetCause(cause), will set the '__cause__' attribute. attribute.
Compatibility Compatibility
=============
Chained exceptions expose the type of the most recent exception, so Chained exceptions expose the type of the most recent exception, so they will
they will still match the same 'except' clauses as they do now. still match the same ``except`` clauses as they do now.
The proposed changes should not break any code unless it sets or The proposed changes should not break any code unless it sets or uses
uses attributes named '__context__', '__cause__', or '__traceback__' attributes named ``__context__``, ``__cause__``, or ``__traceback__`` on
on exception instances. As of 2005-05-12, the Python standard exception instances. As of 2005-05-12, the Python standard library contains no
library contains no mention of such attributes. mention of such attributes.
Open Issue: Extra Information Open Issue: Extra Information
==============================
Walter Dörwald [11] expressed a desire to attach extra information Walter Dörwald [11]_ expressed a desire to attach extra information to an
to an exception during its upward propagation without changing its exception during its upward propagation without changing its type. This could
type. This could be a useful feature, but it is not addressed by be a useful feature, but it is not addressed by this PEP. It could conceivably
this PEP. It could conceivably be addressed by a separate PEP be addressed by a separate PEP establishing conventions for other informational
establishing conventions for other informational attributes on attributes on exceptions.
exceptions.
Open Issue: Suppressing Context Open Issue: Suppressing Context
================================
As written, this PEP makes it impossible to suppress '__context__', As written, this PEP makes it impossible to suppress ``__context__``, since
since setting exc.__context__ to None in an 'except' or 'finally' setting ``exc.__context__`` to ``None`` in an ``except`` or ``finally`` clause
clause will only result in it being set again when exc is raised. will only result in it being set again when ``exc`` is raised.
Open Issue: Limiting Exception Types Open Issue: Limiting Exception Types
=====================================
To improve encapsulation, library implementors may want to wrap all To improve encapsulation, library implementors may want to wrap all
implementation-level exceptions with an application-level exception. implementation-level exceptions with an application-level exception. One could
One could try to wrap exceptions by writing this: try to wrap exceptions by writing this::
try: try:
... implementation may raise an exception ... ... implementation may raise an exception ...
except: except:
import sys import sys
raise ApplicationError from sys.exc_value raise ApplicationError from sys.exc_value
or this: or this::
try: try:
... implementation may raise an exception ... ... implementation may raise an exception ...
except Exception, exc: except Exception, exc:
raise ApplicationError from exc raise ApplicationError from exc
but both are somewhat flawed. It would be nice to be able to name but both are somewhat flawed. It would be nice to be able to name the current
the current exception in a catch-all 'except' clause, but that isn't exception in a catch-all ``except`` clause, but that isn't addressed here.
addressed here. Such a feature would allow something like this: Such a feature would allow something like this::
try: try:
... implementation may raise an exception ... ... implementation may raise an exception ...
except *, exc: except *, exc:
raise ApplicationError from exc raise ApplicationError from exc
Open Issue: yield Open Issue: yield
==================
The exception context is lost when a 'yield' statement is executed; The exception context is lost when a ``yield`` statement is executed; resuming
resuming the frame after the 'yield' does not restore the context. the frame after the ``yield`` does not restore the context. Addressing this
Addressing this problem is out of the scope of this PEP; it is not a problem is out of the scope of this PEP; it is not a new problem, as
new problem, as demonstrated by the following example: demonstrated by the following example::
>>> def gen(): >>> def gen():
... try: ... try:
... 1/0 ... 1/0
... except: ... except:
... yield 3 ... yield 3
... raise ... raise
... ...
>>> g = gen() >>> g = gen()
>>> g.next() >>> g.next()
3 3
>>> g.next() >>> g.next()
TypeError: exceptions must be classes, instances, or strings TypeError: exceptions must be classes, instances, or strings
(deprecated), not NoneType (deprecated), not NoneType
Open Issue: Garbage Collection Open Issue: Garbage Collection
===============================
The strongest objection to this proposal has been that it creates The strongest objection to this proposal has been that it creates cycles
cycles between exceptions and stack frames [12]. Collection of between exceptions and stack frames [12]_. Collection of cyclic garbage (and
cyclic garbage (and therefore resource release) can be greatly therefore resource release) can be greatly delayed.
delayed.
>>> try: ::
>>> 1/0
>>> except Exception, err:
>>> pass
will introduce a cycle from err -> traceback -> stack frame -> err, >>> try:
keeping all locals in the same scope alive until the next GC happens. >>> 1/0
>>> except Exception, err:
>>> pass
Today, these locals would go out of scope. There is lots of code will introduce a cycle from err -> traceback -> stack frame -> err, keeping all
which assumes that "local" resources -- particularly open files -- will locals in the same scope alive until the next GC happens.
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 Today, these locals would go out of scope. There is lots of code which assumes
problems with cyclic garbage. Unfortunately, it would make saving that "local" resources -- particularly open files -- will be closed quickly.
the Exception for later (as unittest does) more awkward, and it would If closure has to wait for the next GC, a program (which runs fine today) may
not allow as much cleanup of the sys module. run out of file handles.
A possible alternate solution, suggested by Adam Olsen, would be to Making the ``__traceback__`` attribute a weak reference would avoid the
instead turn the reference from the stack frame to the 'err' variable problems with cyclic garbage. Unfortunately, it would make saving the
into a weak reference when the variable goes out of scope [13]. ``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 Possible Future Compatible Changes
==================================
These changes are consistent with the appearance of exceptions as These changes are consistent with the appearance of exceptions as a single
a single object rather than a triple at the interpreter level. object rather than a triple at the interpreter level.
- If PEP 340 or PEP 343 is accepted, replace the three (type, value, - If PEP 340 or PEP 343 is accepted, replace the three (``type``, ``value``,
traceback) arguments to __exit__ with a single exception argument. ``traceback``) arguments to ``__exit__`` with a single exception argument.
- Deprecate sys.exc_type, sys.exc_value, sys.exc_traceback, and - Deprecate ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
sys.exc_info() in favour of a single member, sys.exception. ``sys.exc_info()`` in favour of a single member, ``sys.exception``.
- Deprecate sys.last_type, sys.last_value, and sys.last_traceback - Deprecate ``sys.last_type``, ``sys.last_value``, and ``sys.last_traceback``
in favour of a single member, sys.last_exception. in favour of a single member, ``sys.last_exception``.
- Deprecate the three-argument form of the 'raise' statement in - Deprecate the three-argument form of the ``raise`` statement in favour of the
favour of the one-argument form. one-argument form.
- Upgrade cgitb.html() to accept a single value as its first - Upgrade ``cgitb.html()`` to accept a single value as its first argument as an
argument as an alternative to a (type, value, traceback) tuple. alternative to a ``(type, value, traceback)`` tuple.
Possible Future Incompatible Changes 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 - Remove ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``, and
sys.exc_info(). ``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 - Replace the three-argument ``sys.excepthook`` with a one-argument API, and
API, and changing the 'cgitb' module to match. 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' - Upgrade ``traceback.print_exception`` to accept an ``exception`` argument
argument instead of the type, value, and traceback arguments. instead of the ``type``, ``value``, and ``traceback`` arguments.
Implementation Implementation
==============
The __traceback__ and __cause__ attributes and the new raise syntax were The ``__traceback__`` and ``__cause__`` attributes and the new raise syntax
implemented in revision 57783 [14]. were implemented in revision 57783 [14]_.
Acknowledgements Acknowledgements
================
Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip J. Eby,
J. Eby, Raymond Hettinger, Walter Dörwald, and others. Raymond Hettinger, Walter Dörwald, and others.
References References
==========
[1] Raymond Hettinger, "Idea for avoiding exception masking" .. [1] Raymond Hettinger, "Idea for avoiding exception masking"
http://mail.python.org/pipermail/python-dev/2003-January/032492.html http://mail.python.org/pipermail/python-dev/2003-January/032492.html
[2] Brett Cannon explains chained exceptions .. [2] Brett Cannon explains chained exceptions
http://mail.python.org/pipermail/python-dev/2003-June/036063.html http://mail.python.org/pipermail/python-dev/2003-June/036063.html
[3] Greg Ewing points out masking caused by exceptions during finally .. [3] Greg Ewing points out masking caused by exceptions during finally
http://mail.python.org/pipermail/python-dev/2003-June/036290.html http://mail.python.org/pipermail/python-dev/2003-June/036290.html
[4] Greg Ewing suggests storing the traceback in the exception object .. [4] Greg Ewing suggests storing the traceback in the exception object
http://mail.python.org/pipermail/python-dev/2003-June/036092.html http://mail.python.org/pipermail/python-dev/2003-June/036092.html
[5] Guido van Rossum mentions exceptions having a traceback attribute .. [5] Guido van Rossum mentions exceptions having a traceback attribute
http://mail.python.org/pipermail/python-dev/2005-April/053060.html http://mail.python.org/pipermail/python-dev/2005-April/053060.html
[6] Ka-Ping Yee, "Tidier Exceptions" .. [6] Ka-Ping Yee, "Tidier Exceptions"
http://mail.python.org/pipermail/python-dev/2005-May/053671.html http://mail.python.org/pipermail/python-dev/2005-May/053671.html
[7] Ka-Ping Yee, "Chained Exceptions" .. [7] Ka-Ping Yee, "Chained Exceptions"
http://mail.python.org/pipermail/python-dev/2005-May/053672.html http://mail.python.org/pipermail/python-dev/2005-May/053672.html
[8] Guido van Rossum discusses automatic chaining in PyErr_Set* .. [8] Guido van Rossum discusses automatic chaining in ``PyErr_Set*``
http://mail.python.org/pipermail/python-dev/2003-June/036180.html http://mail.python.org/pipermail/python-dev/2003-June/036180.html
[9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism" .. [9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
http://dev.perl.org/perl6/rfc/88.html 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 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 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 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 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 http://svn.python.org/view/python/branches/py3k/Include/?rev=57783&view=rev
Copyright Copyright
=========
This document has been placed in the public domain. This document has been placed in the public domain.
Local Variables: ..
mode: indented-text Local Variables:
indent-tabs-mode: nil mode: indented-text
sentence-end-double-space: t indent-tabs-mode: nil
fill-column: 70 sentence-end-double-space: t
coding: utf-8 fill-column: 70
End: coding: utf-8
End: