A "scopeguard" for Python
Alf P. Steinbach
alfps at start.no
Fri Mar 5 14:34:00 EST 2010
* Steve Howell:
> On Mar 3, 7:10 am, "Alf P. Steinbach" <al... at start.no> wrote:
>> For C++ Petru Marginean once invented the "scope guard" technique (elaborated on
>> by Andrei Alexandrescu, they published an article about it in DDJ) where all you
>> need to do to ensure some desired cleanup at the end of a scope, even when the
>> scope is exited via an exception, is to declare a ScopeGuard w/desired action.
>>
>> The C++ ScopeGuard was/is for those situations where you don't have proper
>> classes with automatic cleanup, which happily is seldom the case in good C++
>> code, but languages like Java and Python don't support automatic cleanup and so
>> the use case for something like ScopeGuard is ever present.
>>
>> For use with a 'with' statement and possibly suitable 'lambda' arguments:
>>
>> <code>
>> class Cleanup:
>> def __init__( self ):
>> self._actions = []
>>
>> def call( self, action ):
>> assert( is_callable( action ) )
>> self._actions.append( action )
>>
>> def __enter__( self ):
>> return self
>>
>> def __exit__( self, x_type, x_value, x_traceback ):
>> while( len( self._actions ) != 0 ):
>> try:
>> self._actions.pop()()
>> except BaseException as x:
>> raise AssertionError( "Cleanup: exception during cleanup" ) from
>> </code>
>>
>> I guess the typical usage would be what I used it for, a case where the cleanup
>> action (namely, changing back to an original directory) apparently didn't fit
>> the standard library's support for 'with', like
>>
>> with Cleanup as at_cleanup:
>> # blah blah
>> chdir( somewhere )
>> at_cleanup.call( lambda: chdir( original_dir ) )
>> # blah blah
>>
>> Another use case might be where one otherwise would get into very deep nesting
>> of 'with' statements with every nested 'with' at the end, like a degenerate tree
>> that for all purposes is a list. Then the above, or some variant, can help to
>> /flatten/ the structure. To get rid of that silly & annoying nesting. :-)
>>
>> Cheers,
>>
>> - Alf (just sharing, it's not seriously tested code)
>
> Hi Alf, I think I understand the notion you're going after here. You
> have multiple cleanup steps that you want to defer till the end, and
> there is some possibility that things will go wrong along the way, but
> you want to clean up as much as you can. And, of course, flatter is
> better.
>
> Is this sort of what you are striving for?
>
> class Cleanup:
> def __init__( self ):
> self._actions = []
>
> def call( self, action ):
> self._actions.append( action )
>
> def __enter__( self ):
> return self
>
> def __exit__( self, x_type, x_value, x_traceback ):
> while( len( self._actions ) != 0 ):
> try:
> self._actions.pop()()
> except BaseException as x:
> raise AssertionError( "Cleanup: exception during
> cleanup" )
>
> def clean_the_floor():
> print('clean the floor')
>
> def carouse(num_bottles, accident):
> with Cleanup() as at_cleanup:
> at_cleanup.call(clean_the_floor)
> for i in range(num_bottles):
> def take_down(i=i):
> print('take one down', i)
> at_cleanup.call(take_down)
> if i == accident:
> raise Exception('oops!')
> print ('put bottle on wall', i)
>
> carouse(10, None)
> carouse(5, 3)
He he.
I think you meant:
> def carouse(num_bottles, accident):
> with Cleanup() as at_cleanup:
> at_cleanup.call(clean_the_floor)
> for i in range(num_bottles):
> def take_down(i=i):
> print('take one down', i)
> if i == accident:
> raise Exception('oops!')
> print ('put bottle on wall', i)
> at_cleanup.call(take_down)
I'm not sure. It's interesting & fun. But hey, it's Friday evening.
Regarding the "clean the floor", Marginean's original ScopeGuard has a 'dismiss'
method (great for e.g. implementing transactions). There's probably also much
other such functionality that can be added.
The original use case for Cleanup, I just posted this in case people could find
it useful, was a harness for testing that C++ code /fails/ as it should,
<url: http://pastebin.com/NK8yVcyv>, where Cleanup is used at line 479.
Some discussion of that in Usenet message and associated thread
<hmmcdm$p1i$1 at news.eternal-september.org>, "Unit testing of expected failures --
what do you use?" in [comp.lang.c++].
Another similar approach was discussed by Carlo Milanesi in <url:
http://www.drdobbs.com/cpp/205801074>, but he supplied this reference after I'd
done the above. Mainly the difference is that he defines a custom mark-up
language with corresponding source preprocessing, while I use the ordinary C++
preprocessor. There are advantages and disadvantages to both approaches.
Cheers,
- Alf
More information about the Python-list
mailing list