[Python-3000] Pre-peps on raise and except changes (was: Warning for 2.6 and greater)

Collin Winter collinw at gmail.com
Wed Jan 24 00:23:43 CET 2007


On 1/22/07, Phillip J. Eby <pje at telecommunity.com> wrote:
> The correct translation of a try/except should be:
>
>      try:
>          ...
>      except E as N:
>          try:
>              ...
>          finally:
>              N = None
>              del N
>
> i.e., you left out the crucial try/finally wrapper.  If you got this
> translation from the current implementation, then the implementation is
> wrong too.  :)

Yikes! The implementation is right, it's my memory that's bad : )

> Also, the PEP should discuss the matter of how "except" should change in
> Python 2.6 to support this, including the current open issue of handling
> the .traceback attribute.

I believe it's been suggested that 2.x's "except" will support both
"," and "as" between the exception class and the target, but I don't
recall any explicit approval for that course of action.

> The 2-expression "raise" statement translation is incorrect in the general
> case; it is possible for the second argument to be an instance of the first
> argument, in which case 'raise E, V' should become just 'raise V'.  This is
> not detectable by the refactoring tool, AFAIK.

It's possible, but rare in practice. I've run across only nine such
usages in the entire standard distribution. They break down into two
camps:

As a synonym for a zero-argument raise:
    Demo/pysvr/pysvr.py
    Lib/re.py

Casting an exception from one type to another:
    Tools/webchecker/webchecker.py
    Lib/os.py
    Lib/pty.py
    Lib/distutils/bcppcompiler.py
    Lib/distutils/unixccompiler.py
    Lib/distutils/fancy_getopt.py
    Lib/distutils/emxccompiler.py

These latter usages could all be expressed as "raise E from V"
(starting as soon as that gets implemented). I've added language to
the "raise" PEP detailing how to convert these to "raise ... from" for
Python 3.

The latest version of both PEPs are attached.

Thanks for the feedback!

Collin Winter
-------------- next part --------------
PEP: 3109
Title: Catching Exceptions in Python 3000
Version: $Revision$
Last-Modified: $Date$
Author: Collin Winter <collinw at gmail.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 16-Jan-2006
Python-Version: 3.0
Post-History:


Abstract
========

This PEP introduces changes intended to help eliminate ambiguities
in Python's grammar, simplify exception classes, simplify garbage
collection for exceptions and reduce the size of the language in
Python 3.0.


Rationale
=========

1. ``except`` clauses in Python 2.x present a syntactic ambiguity
   where the parser cannot differentiate whether ::

       except <expression>, <expression>:
    
   should be interpreted as ::

       except <type>, <type>:
    
   or ::

       except <type>, <name>:

   Python 2 opts for the latter semantic, at the cost of requiring the
   former to be parenthesized, like so ::

    except (<type>, <type>):

2. As specified in PEP 352 [#pep352]_, the ability to treat exceptions
   as tuples will be removed, meaning this code will no longer work ::

       except os.error, (errno, errstr):
    
   Because the automatic unpacking will no longer be possible, it is
   desirable to remove the ability to use tuples as ``except`` targets.

3. As specified in PEP 344 [#pep344]_, exception instances in Python 3
   will possess a ``__traceback__`` attribute. The Open Issues section
   of that PEP includes a paragraph on garbage collection difficulties
   caused by this attribute, namely a "exception -> traceback ->
   stack frame -> exception" reference cycle, whereby all locals are 
   kept in scope until the next GC run. This PEP intends to resolve
   this issue by adding a cleanup semantic to ``except`` clauses in
   Python 3 whereby the target name is deleted at the end of the
   ``except`` suite.
   
4. In the spirit of "there should be one -- and preferably only one
   -- obvious way to do it" [#zen]_, it is desirable to consolidate
   duplicate functionality. To this end, the ``exc_value``,
   ``exc_type`` and ``exc_traceback`` attributes of the ``sys``
   module [#sys-module]_ will be removed in favor of
   ``sys.exc_info()``, which provides the same information. These
   attributes are already listed in PEP 3100 [#pep3100]_ as targeted
   for removal.


Grammar Changes
===============
    
In Python 3, the grammar for ``except`` statements will change
from [#grammar]_ ::

    except_clause: 'except' [test [',' test]]
    
to ::

    except_clause: 'except' [test ['as' NAME]]

The use of ``as`` in place of the comma token means that ::

    except AttributeError, os.error:
    
can be clearly understood as a tuple of exception classes. This new
syntax was first proposed by Greg Ewing [#firstproposal]_ and
endorsed ([#firstproposal]_, [#renaming]_) by the BDFL.

Further, the restriction of the token following ``as`` from ``test``
to ``NAME`` means that only valid identifiers can be used as
``except`` targets.


Semantic Changes
================

In order to resolve the garbage collection issue related to PEP 344,
``except`` statements will generate additional bytecode to delete the
target, thus eliminating the reference cycle. The source-to-source
translation, as suggested by Phillip J. Eby [#except-translation]_ is
::

    try:
        ...
    except E as N:
        ...
    ...
    
is translated to ::

    try:
        ...
    except E as N:
        try:
            ...
        finally:
            N = None
            del N
    ...

An implementation has already been checked into the p3yk branch
[#translation-checkin]_.


Compatibility Issues
====================

Nearly all ``except`` clauses will need to be changed. ``except``
clauses with identifier targets will be converted from ::

    except E, N:
    
to ::
    
    except E as N:

``except`` clauses with non-tuple, non-identifier targets
(e.g., ``a.b.c[d]``) will need to be converted from ::

    except E, T:
    
to ::

    except E as t:
        T = t
        
Both of these cases can be handled by Guido van Rossum's ``2to3``
utility [#2to3]_ using the ``except`` fixer [#exceptfixer]_.
        
``except`` clauses with tuple targets will need to be converted
manually, on a case-by-case basis. These changes will usually need
to be accompanied by changes to the exception classes themselves.
While these changes generally cannot be automated, the ``2to3``
utility is able to point out cases where the target of an ``except``
clause is a tuple, simplifying conversion.

Situations where it is necessary to keep an exception instance around
past the end of the ``except`` suite can be easily translated like so
::

    try:
        ...
    except E as N:
        ...
    ...
    
is translated to ::

    try:
        ...
    except E as N:
        n = N
        ...
    ...
    
This way, when ``N`` is deleted at the end of the block, ``n`` will
persist and can be used as normal.

Lastly, all uses of the ``sys`` module's ``exc_type``, ``exc_value``
and ``exc_traceback`` attributes will need to be removed. They can be
replaced with ``sys.exc_info()[0]``, ``sys.exc_info()[1]`` and
``sys.exc_info()[2]`` respectively, a transformation that can be
performed by ``2to3``'s ``sysexcinfo`` fixer.


Open Issues
===========

"except" Statements in Python 2.x
-----------------------------------

It has been proposed that the grammar for ``except`` statements be
changed to accommodate both Python 2's ``,`` and Python 3's ``as``
between the statement's type and target portions. The grammar
would thus change from ::

    except_clause: 'except' [test [',' test]]

to ::

    except_clause: 'except' [test [('as' | ',') test]]

It has not been decided whether the proposed end-of-suite cleanup
semantic for ``except`` statements should be included in the 2.x
series.


References
==========

.. [#pep352]
   http://www.python.org/dev/peps/pep-0352/
   
.. [#zen]
   http://www.python.org/dev/peps/pep-0020/
   
.. [#sys-module]
   http://docs.python.org/lib/module-sys.html
   
.. [#pep3100]
   http://www.python.org/dev/peps/pep-3100/
   
.. [#pep344]
   http://www.python.org/dev/peps/pep-0344/

.. [#firstproposal]
   http://mail.python.org/pipermail/python-dev/2006-March/062449.html

.. [#renaming]
   http://mail.python.org/pipermail/python-dev/2006-March/062640.html
   
.. [#grammar]
   http://www.python.org/doc/current/ref/try.html
   
.. [#except-translation]
   http://mail.python.org/pipermail/python-3000/2007-January/005395.html
   
.. [#translation-checkin]
   http://svn.python.org/view?rev=53342&view=rev
   
.. [#2to3]
   http://svn.python.org/view/sandbox/trunk/2to3/
   
.. [#exceptfixer]
   http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_except.py


Copyright
=========

This document has been placed in the public domain.



..
   Local Variables:
   mode: indented-text
   indent-tabs-mode: nil
   sentence-end-double-space: t
   fill-column: 70
   coding: utf-8
   End:
-------------- next part --------------
PEP: 3109
Title: Raising Exceptions in Python 3000
Version: $Revision$
Last-Modified: $Date$
Author: Collin Winter <collinw at gmail.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 19-Jan-2006
Python-Version: 3.0
Post-History:


Abstract
========

This PEP introduces changes to Python's mechanisms for raising
exceptions intended to reduce both line noise and the size of the
language.


Rationale
=========

One of Python's guiding maxims is "there should be one -- and
preferably only one -- obvious way to do it" [#zen]_. Python 2.x's
``raise`` statement violates this principle, permitting multiple
ways of expressing the same thought. For example, these statements
are equivalent: ::
    
    raise E, V
    
    raise E(V)
    
There is a third form of the ``raise`` statement, allowing arbitrary
tracebacks to be attached to an exception [#grammar]_: ::

    raise E, V, T
    
where T is a traceback. As specified in PEP 344 [#pep344]_,
exception objects in Python 3.x will possess a ``__traceback__``
attribute, admitting this translation of the three-expression
``raise`` statement: ::

    raise E, V, T
    
is translated to ::

    e = E(V)
    e.__traceback__ = T
    raise e
    
Using these translations, we can reduce the ``raise`` statement from
four forms to two:

1. ``raise`` (with no arguments) is used to re-raise the active
   exception in an ``except`` suite.
   
2. ``raise EXCEPTION`` is used to raise a new exception. This form has
   two sub-variants: ``EXCEPTION`` may be either an instance of
   ``BaseException`` or a subclass of ``BaseException`` [#pep352]_.
   If ``EXCEPTION`` is a subclass, it will be called with no arguments
   to obtain an exception instance.
   
   To raise anything else is an error.

There is a further, more tangible benefit to be obtained through this
consolidation, as noted by A.M. Kuchling [#amk-line-noise]_. ::

    PEP 8 doesn't express any preference between the 
    two forms of raise statements:
    raise ValueError, 'blah'
    raise ValueError("blah")

    I like the second form better, because if the exception arguments
    are long or include string formatting, you don't need to use line
    continuation characters because of the containing parens.
    
The BDFL has concurred [#guido-declaration]_ and endorsed the
consolidation of the several ``raise`` forms.
    

Grammar Changes
===============
    
In Python 3, the grammar for ``raise`` statements will change
from [#grammar]_ ::

    raise_stmt: 'raise' [test [',' test [',' test]]]
    
to ::

    raise_stmt: 'raise' [test]
    
    
Changes to Builtin Types
========================
    
Because of its relation to exception raising, the signature for the
``throw()`` method on generator objects will change, dropping the
optional second and third parameters. The signature thus changes 
from [#throw-sig]_ ::

    generator.throw(E, [V, [T]])
    
to ::

    generator.throw(EXCEPTION)
    
Where ``EXCEPTION`` is either a subclass of ``BaseException`` or an
instance of a subclass of ``BaseException``.

    
Semantic Changes
================

In Python 2, the following ``raise`` statement is legal ::

    raise ((E1, (E2, E3)), E4), V
    
The interpreter will take the tuple's first element as the exception
type (recursively), making the above fully equivalent to ::

    raise E1, V
    
As of Python 3.0, support for raising tuples like this will be
dropped. This change will bring ``raise`` statements into line with
the ``throw()`` method on generator objects, which already disallows
this.
    

Compatibility Issues
====================

All two- and three-expression ``raise`` statements will require
modification, as will all two- and three-expression ``throw()`` calls
on generators. Fortunately, the translation from Python 2.x to
Python 3.x in this case is simple and can be handled mechanically
by Guido van Rossum's 2to3 utility [#2to3]_ using the ``raise`` and
``throw`` fixers ([#raise-fixer]_, [#throw-fixer]_).

The following translations will be performed:

1. Zero- and one-expression ``raise`` statements will be left
   intact.
   
2. Two-expression ``raise`` statements will be converted from ::
        
        raise E, V
        
   to ::
   
        raise E(V)
        
   Two-expression ``throw()`` calls will be converted from ::
        
        generator.throw(E, V)
        
   to ::
   
        generator.throw(E(V))
        
   See point #5 for a caveat to this transformation.
        
3. Three-expression ``raise`` statements will be converted from ::

        raise E, V, T
        
   to ::
   
        e = E(V)
        e.__traceback__ = T
        raise e
        
   Three-expression ``throw()`` calls will be converted from ::
   
        generator.throw(E, V, T)
        
   to ::
   
        e = E(V)
        e.__traceback__ = T
        generator.throw(e)
        
   See point #5 for a caveat to this transformation.
        
4. Two- and three-expression ``raise`` statements where ``E`` is a
   tuple literal can be converted automatically using ``2to3``'s
   ``raise`` fixer. ``raise`` statements where ``E`` is a non-literal
   tuple, e.g., the result of a function call, will need to be
   converted manually.
   
5. Two- and three-expression ``raise`` statements where ``E`` is an
   exception class and ``V`` is an exception instance will need
   special attention. These cases break down into two camps:
   
   1. ``raise E, V`` as a long-hand version of the zero-argument
      ``raise`` statement. As an example, assuming F is a subclass
      of E ::
      
          try:
              something()
          except F as V:
              raise F(V)
          except E as V:
              handle(V)
              
      This would be better expressed as ::
      
          try:
              something()
          except F:
              raise
          except E as V:
              handle(V)
              
    2. ``raise E, V`` as a way of "casting" an exception to another
       class. Taking an example from
       distutils.compiler.unixcompiler ::
       
            try:
                self.spawn(pp_args)
            except DistutilsExecError as msg:
                raise CompileError(msg)
                
       This would be better expressed as ::
       
            try:
                self.spawn(pp_args)
            except DistutilsExecError as msg:
                raise CompileError from msg
   
       Using the ``raise ... from ...`` syntax introduced in
       PEP 344.


References
==========

.. [#zen]
   http://www.python.org/dev/peps/pep-0020/
   
.. [#grammar]
   http://www.python.org/doc/current/ref/raise.html
   
.. [#throw-sig]
   http://www.python.org/dev/peps/pep-0342/
   
.. [#pep344]
   http://www.python.org/dev/peps/pep-0344/
   
.. [#pep352]
   http://www.python.org/dev/peps/pep-0352/
   
.. [#amk-line-noise]
   http://mail.python.org/pipermail/python-dev/2005-August/055187.html

.. [#guido-declaration]
   http://mail.python.org/pipermail/python-dev/2005-August/055190.html
   
.. [#2to3]
   http://svn.python.org/view/sandbox/trunk/2to3/
   
.. [#raise-fixer]
   http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_raise.py
   
.. [#throw-fixer]
   http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_throw.py


Copyright
=========

This document has been placed in the public domain.



..
   Local Variables:
   mode: indented-text
   indent-tabs-mode: nil
   sentence-end-double-space: t
   fill-column: 70
   coding: utf-8
   End:


More information about the Python-3000 mailing list