[Python-ideas] ScopeGuardStatement/Defer Proposal

Nick Coghlan ncoghlan at gmail.com
Fri Feb 17 02:33:59 CET 2012

On Fri, Feb 17, 2012 at 10:06 AM, Manuel Barkhau
<mbarkhau at googlemail.com> wrote:
> It is also similar to golangs "defer" statement:
> http://golang.org/doc/go_spec.html#Defer_statements

Since there have been a few proposals along these lines recently:

Nothing is going to happen on the dedicated syntax front in the
deferred execution space at least until I get contextlib.CallbackStack
into Python 3.3 and we gather additional feedback on patterns of use
(and, assuming that API addresses the relevant use cases the way I
plan, these features will *never* need dedicated syntax).

A preliminary version of the API is available in the contextlib2
backport as ContextStack:

See the issue tracker for the changes that are planned in order to
update that to the new CallbackStack API:

> Some example code:
>>>> def ordering_example():
> ...     print(1)
> ...     defer: print(2)
> ...     defer: print(3)
> ...     print(4)

With ContextStack:

def ordering_example():
    with ContextStack() as stack:
        stack.register(print, 2)
        stack.register(print, 3)

With the planned CallbackStack API:

def ordering_example():
    with CallbackStack() as stack:
        stack.push(print, 2)
        stack.push(print, 3)

>>>> ordering_example()
> 1
> 4
> 3
> 2

Same output.

> The nesting advantage becomes more apparent when more are required. Here
> is an example from
> http://www.doughellmann.com/articles/how-tos/python-exception-handling/index.html
>    #!/usr/bin/env python
>    import sys
>    import traceback
>    def throws():
>        raise RuntimeError('error from throws')
>    def cleanup():
>        raise RuntimeError('error from cleanup')
>    def nested():
>        try:
>            throws()
>        except Exception as original_error:
>            try:
>                raise
>            finally:
>                try:
>                    cleanup()
>                except:
>                    pass # ignore errors in cleanup

Huh? That's a bizarre way to write it. A more sane equivalent would be

    def nested():
        except BaseException:

>>> def throws():
...     1/0
>>> def nested():
...     try:
...         throws()
...     except BaseException:
...         try:
...             raise Exception
...         except:
...             pass
...         raise
>>> nested()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in nested
  File "<stdin>", line 2, in throws
ZeroDivisionError: division by zero

However, this does raise a reasonable feature request for the planned
contextlib2.CallbackStack API, so the above can be written as:

    def _ignore_exception(*args):
        return True

    def _cleanup_on_error(exc_type, exc_val, exc_tb):
        if exc_type is not None:

    def nested():
        with CallbackStack(callback_error=_ignore_exception) as stack:

In Python 3 though, your better bet is often going to be just to let
the cleanup exception fly - the __context__ attribute means the
original exception and the full stack trace will be preserved

> I'm sure there is much I have overlooked, possibly this is technically
> difficult and of course there is the minor task of implementation. But
> other than that what do you think?

I think contextlib2 and PEP 3144 cover the use cases you have
presented more cleanly and without drastic syntax changes.


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-ideas mailing list