Skip to content

Commit

Permalink
Write some more on exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
encukou committed Mar 29, 2016
1 parent a0a4b3a commit 314e128
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 8 deletions.
1 change: 1 addition & 0 deletions source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
# ones.
extensions = [
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
]

# Add any paths that contain templates here, relative to this directory.
Expand Down
3 changes: 3 additions & 0 deletions source/core-obj-misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ Function Attributes
``__getslice__``, ``__setslice__``, ``__delslice__``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``__bool__``
~~~~~~~~~~~~

Unbound Methods
~~~~~~~~~~~~~~~
118 changes: 117 additions & 1 deletion source/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,136 @@ To fix this, Python 2.6 introduced an alternate syntax:
In Python 3, the old syntax is no longer allowed.

You will need to switch to the new syntax.
The recommended fixer works quite reliably.
The recommended fixer works quite reliably, and it also fixes the
:ref:`iter_exc` problem described below.


.. _raise-syntax:

The new ``raise`` syntax
~~~~~~~~~~~~~~~~~~~~~~~~~

* :ref:`Fixer <python-modernize>`: ``python-modernize -wnf libmodernize.fixes.fix_raise -f libmodernize.fixes.fix_raise_six``
* Prevalence: Common

Python 2's ``raise`` statement was designed at a time when exceptions weren't
classes, and an exception's *type*, *value*, and *traceback* components
were three separate objects::

raise ValueError, 'invalid input'
raise ValueError, 'invalid input', some_traceback

In Python 3, one single object includes all information about an exception::

raise ValueError('invalid input')

e = ValueError('invalid input')
e.__traceback__ = some_traceback
raise e

Python 2.6 allows the first variant. For the second, reraising an exception,
the :ref:`six` library includes a convenience wrapper that works in both
versions::

import six
six.reraise(ValueError, 'invalid input', some_traceback)

The recommended fixers will do these conversions automatically and quite
reliably, but do verify the resulting changes.


.. _exc_scope:
.. index::
single: NameError (from caught exception)

Caught Exception “Scope”
~~~~~~~~~~~~~~~~~~~~~~~~

* :ref:`Fixer <python-modernize>`: *None*
* Prevalence: Rare

As :ref:`discussed previously <raise-syntax>`, in Python 3, all information
about an exception, including the traceback, is contained in the exception
object.
Since, the traceback holds references to the values of all local variables,
storing an exception in a local variable usually forms a reference cycle,
keeping all local variables allocated until the next garbage collection pass.

To prevent this issue, to quote from :py3:ref:`Python's documentation <try>`:

When an exception has been assigned using as target, it is cleared at
the end of the except clause. This is as if ::

except E as N:
foo

was translated to ::

except E as N:
try:
foo
finally:
del N

This means the exception must be assigned to a different name to be
able to refer to it after the except clause.

Unfortunately, :ref:`python-modernize` does not provide a fixer for this
change.
This issue results in a loud ``NameError`` when tests are run.


.. _iter_exc:

Iterating Exceptions
~~~~~~~~~~~~~~~~~~~~

* :ref:`Fixer <python-modernize>`: ``python-modernize -wnf libmodernize.fixes.fix_except``
* Prevalence: Rare

In Python 2, exceptions were *iterable*. so it was possible to “unpack” the
arguments of an exception as part of the ``except`` statement::

except RuntimeError as (num, message):

In Python 3, this is no longer true.

except RuntimeError as e:
num, message = e.args

The reccommended fixer catches the easy cases of unpacking in ``except``
statements.
If your code iterates through exceptions elsewhere, you need to manually
change it to iterate over ``args`` instead.

Additionally, the fixer does not do a good job on single-line suites such as::

except RuntimeError as (num, message): pass

Inspect the output and break these into multiple lines manually.

.. todo:: Report bug to python-modernize


Raising Non-Exceptions
~~~~~~~~~~~~~~~~~~~~~~

* Fixer: None
* Prevalence: Rare

In Python 3, an object used with ``raise`` must be an instance of
``BaseException``, while Python 2 also allowed old-style classes.

XXX

.. todo:: Link "old-style classes" to their section


The Removed ``StandardError``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



Removed ``sys.exc_type``, ``sys.exc_value``, ``sys.exc_traceback``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

12 changes: 5 additions & 7 deletions source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,12 @@ The porting process
both major Python versions.


.. comment:
Indices and tables
==================

Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

.. _Python 3 Q & A: http://python-notes.curiousefficiency.org/en/latest/python3/questions_and_answers.html
.. _foreword of Lennart Regebro's book: http://python3porting.com/foreword.html
Expand Down

0 comments on commit 314e128

Please sign in to comment.