A "scopeguard" for Python

Alf P. Steinbach alfps at start.no
Fri Mar 5 14:13:23 EST 2010


* Mike Kent:
> On Mar 4, 8:04 pm, Robert Kern <robert.k... at gmail.com> wrote:
>>> No, the try: finally: is not implicit. See the source for
>> contextlib.GeneratorContextManager. When __exit__() gets an exception from the
>> with: block, it will push it into the generator using its .throw() method. This
>> raises the exception inside the generator at the yield statement.
>> Wow, I just learned something new. My understanding of context
> managers was that the __exit__ method was guaranteed to be executed
> regardless of how the context was left. I have often written my own
> context manager classes, giving them the __enter__ and __exit__
> methods. I had mistakenly assumed that the @contextmanager decorator
> turned a generator function into a context manager with the same
> behavior as the equivalent context manager class. Now I learn that,
> no, in order to have the 'undo' code executed in the presence of an
> exception, you must write your own try/finally block in the generator
> function.
>> This raises the question in my mind: What's the use case for using
> @contextmanager rather than wrapping your code in a context manager
> class that defines __enter__ and __exit__, if you still have to
> manager your own try/finally block?

Robert Kern and Steve Howell have already given given good answers.
As it happened this was news to me also, because I'm not that well-versed in 
Python and it seems contrary to the purpose of providing a simpler way to write 
a simple init-cleanup wrapper.
But additionally, if you want that, then you can define it, e.g.
<code>
# Py3
def simplecleanup( generator_func ):
 class SimpleCleanup:
 def __init__( self, *args, **kwargs ):
 self.generator = generator_func( *args, **kwargs )
 def __enter__( self ):
 self.generator.send( None )
 return self
 def __exit__( self, x_type, x_obj, x_traceback ):
 try:
 self.generator.send( x_obj ) # x_obj is None if no exception
 except StopIteration:
 pass # Expected
 return SimpleCleanup
@simplecleanup
def hello_goodbye( name ):
 print( "Hello, {}!".format( name ) )
 yield
 print( "Goodbye {}!".format( name ) )
try:
 with hello_goodbye( "Mary" ):
 print( "Talk talk talk..." )
 raise RuntimeError( "Offense" )
except:
 pass
print()
@simplecleanup
def sensitive_hello_goodbye( name ):
 print( "Hello, {}!".format( name ) )
 x = yield
 if x is not None:
 print( "Uh oh, {}!".format( x ) )
 print( "Good day {}!".format( name ) )
 else:
 print( "C u, {}!".format( name ) )
try:
 with sensitive_hello_goodbye( "Jane" ):
 print( "Talk talk talk..." )
 raise RuntimeError( "Offense" )
except:
 pass
</code>
Cheers,
- Alf


More information about the Python-list mailing list

AltStyle によって変換されたページ (->オリジナル) /