A "scopeguard" for Python
robert.kern at gmail.com
Fri Mar 5 18:00:22 CET 2010
On 2010-03-05 10:29 AM, Mike Kent wrote:
> 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.
It is. @contextmanager turns a specially-written generator into a context
manager with an __exit__ that does different things depending on whether or not
and exception was raised. By pushing the exception into the generator, it lets
the author decide what to do. It may catch a subset of exceptions, or no
exceptions, or use a finally:. They all have use cases although finally: is the
> 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.
Basically, it does. __exit__() is given the exception information. When you
write such a class, you can decide what to do with the exception. You can
silence it, immediately reraise it, conditionally reraise it, log it and then
reraise it, etc. Pushing the exception into the generator keeps this flexibility
and the equivalence. If it removed that choice, then it would not be equivalent.
> 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
> 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?
The @contextmanager generator implementations are often shorter and easier to
read, in my opinion, partly because they use the try: finally: syntax that most
of us are very familiar with. I have to think less when I read it because it
looks so similar to the equivalent code that you would normally write.
The point of context managers isn't to remove the use of try: finally: entirely,
but to implement it once so that it can be reused cleanly. You only have to
write the one try: finally: in the generator and reuse it simply with the with:
statement in many places.
"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
More information about the Python-list