[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:
http://contextlib2.readthedocs.org/en/latest/index.html#contextlib2.ContextStack

See the issue tracker for the changes that are planned in order to
update that to the new CallbackStack API:
https://bitbucket.org/ncoghlan/contextlib2/issue/8/rename-contextstack-to-callbackstack-and

> 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:
        print(1)
        stack.register(print, 2)
        stack.register(print, 3)
        print(4)


With the planned CallbackStack API:

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

>>>> 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():
        try:
            throws()
        except BaseException:
            try:
                cleanup()
            except:
                pass
            raise

>>> 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:
            cleanup()

    def nested():
        with CallbackStack(callback_error=_ignore_exception) as stack:
            stack.push_exit(_cleanup_on_error)
            throws()

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
automatically.

> 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.

Cheers,
Nick.

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



More information about the Python-ideas mailing list