Why does my contextmanager-function not work like my contextmanager class in python?
Question: In my code, I need to be able to open and close a device properly, and
therefore see the need to use a context manager. While a context manager is
usually defined as a class with `__enter__` and `__exit__` methods, there also
seem to be the possibility to decorate a function for use with the context
manager (see [a recent post](http://stackoverflow.com/questions/15427361/how-
to-use-the-context-manager-to-avoid-the-use-of-del-in-
python/15427892?noredirect=1#15427892) and [another nice example
here](http://preshing.com/20110920/the-python-with-statement-by-example)).

In the following (working) code snippet, I have implemented the two
possibilities; one just need to swap the commented line with the other one:

    
    
    import time
    import contextlib
    
    def device():
        return 42
    
    @contextlib.contextmanager
    def wrap():
        print("open")
        yield device
        print("close")
        return
    
    class Wrap(object):
        def __enter__(self):
            print("open")
            return device
        def __exit__(self, type, value, traceback):
            print("close")
    
    
    #with wrap() as mydevice:
    with Wrap() as mydevice:
        while True:
            time.sleep(1)
            print mydevice()
    

What I try is to run the code and stop it with `CTRL-C`. When I use the `Wrap`
class in the context manager, the `__exit__` method is called as expeced (the
text 'close' is printed in the terminal), but when I try the same thing with
the `wrap` function, the text 'close' is not printed to the terminal.

My question: Is there a problem with the code snippet, am I missing something,
or why is the line `print("close")` not called with the decorated function?


Answer: The example in the documentation for `contextmanager` is somewhat misleading.
The portion of the function after `yield` does not really correspond to the
`__exit__` of the context manager protocol. The key point in the documentation
is this:

> If an unhandled exception occurs in the block, it is reraised inside the
> generator at the point where the yield occurred. Thus, you can use a
> `try...except...finally` statement to trap the error (if any), or ensure
> that some cleanup takes place.

So if you want to handle an exception in your contextmanager-decorated
function, you need to write your own `try` that wraps the `yield` and handle
the exceptions yourself, executing cleanup code in a `finally` (or just block
the exception in `except` and execute your cleanup after the `try/except`).
For example:

    
    
    @contextlib.contextmanager
    def cm():
        print "before"
        exc = None
        try:
            yield
        except Exception, exc:
            print "Exception was caught"
        print "after"
        if exc is not None:
            raise exc
    
    >>> with cm():
    ...     print "Hi!"
    before
    Hi!
    after
    
    >>> with cm():
    ...     print "Hi!"
    ...     1/0
    before
    Hi!
    Exception was caught
    after
    

[This page](http://pymotw.com/2/contextlib/index.html#from-generator-to-
context-manager) also shows an instructive example.

