[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