On 2010-03-05 10:29 AM, Mike Kent wrote:
On Mar 4, 8:04 pm, Robert Kern<robert.k...@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 usual one.

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

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.

--
Robert Kern

"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

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to