.. Copyright 2009-2017 Ram Rachum. This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License, with attribution to "Ram Rachum at ram.rachum.com" including link. The license may be obtained at http://creativecommons.org/licenses/by-sa/3.0/ .. _topics-context-management: :mod:`context_management` ========================= Context managers are awesome ---------------------------- I love context managers, and I love the :keyword:`with` keyword. If you've never dealt with context managers or :keyword:`with`, `here's a practical guide which explains how to use them.`_ You may also read the more official :pep:`343` which introduced these features to the language. Using :keyword:`with` and context managers in your code contributes a lot to making your code more beautiful and maintainable. Every time you replace a :keyword:`try`\-:keyword:`finally` clause with a :keyword:`with` clause, an angel gets a pair of wings. Now, you don't *need* any official :class:`ContextManager` class in order to use context managers or define them; you just need to define :meth:`__enter__` and :meth:`__exit__` methods in your class, and then you can use your class as a context manager. *But*, if you use the :class:`ContextManager` class as a base class to your context manager class, you could enjoy a few more features that might make your code a bit more concise and elegant. What does :class:`ContextManager` add? -------------------------------------- The :class:`ContextManager` class allows using context managers as decorators (in addition to their normal use) and supports writing context managers in a new form called :meth:`manage_context`. (As well as the original forms). First let's import: >>> from python_toolbox import context_management Now let's go over the features one by one. The :class:`ContextManager` class allows you to **define** context managers in new ways and to **use** context managers in new ways. I'll explain both of these; let's start with **defining** context managers. Defining context managers ------------------------- There are 3 different ways in which context managers can be defined, and each has their own advantages and disadvantages over the others. - The classic way to define a context manager is to define a class with :meth:`__enter__` and :meth:`__exit__` methods. This is allowed, and if you do this you should still inherit from :class:`ContextManager`. Example: >>> class MyContextManager(context_management.ContextManager): ... def __enter__(self): ... pass # preparation ... def __exit__(self, type_=None, value=None, traceback=None): ... pass # cleanup - As a decorated generator, like so: >>> @context_management.ContextManagerType ... def MyContextManager(): ... # preparation ... try: ... yield ... finally: ... pass # cleanup The advantage of this approach is its brevity, and it may be a good fit for relatively simple context managers that don't require defining an actual class. This usage is nothing new; it's also available when using the standard library's :func:`contextlib.contextmanager` decorator. One thing that is allowed here that :mod:`contextlib` doesn't allow is to yield the context manager itself by doing ``yield context_management.SelfHook``. - The third and novel way is by defining a class with a :meth:`manage_context` method which returns a decorator. Example: >>> class MyContextManager(ContextManager): ... def manage_context(self): ... do_some_preparation() ... with other_context_manager: ... yield self This approach is sometimes cleaner than defining :meth:`__enter__` and :meth:`__exit__`; especially when using another context manager inside :meth:`manage_context`. In our example we did ``with other_context_manager`` in our :meth:`manage_context`, which is shorter, more idiomatic and less double-underscore-y than the equivalent classic definition: >>> class MyContextManager(object): ... def __enter__(self): ... do_some_preparation() ... other_context_manager.__enter__() ... return self ... def __exit__(self, *exc): ... return other_context_manager.__exit__(*exc) Another advantage of the :meth:`manage_context` approach over :meth:`__enter__` and :meth:`__exit__` is that it's better at handling exceptions, since any exceptions would be raised inside :meth:`manage_context` where we could :keyword:`except` them, which is much more idiomatic than the way :meth:`__exit__` handles exceptions, which is by receiving their type and returning whether to swallow them or not. These were the different ways of defining a context manager. Now let's see the different ways of **using** a context manager: Using context managers ---------------------- There are 2 different ways in which context managers can be used: - The plain old honest-to-Guido :keyword:`with` keyword: >>> with MyContextManager() as my_context_manager: ... do_stuff() - As a decorator to a function: >>> @MyContextManager() ... def do_stuff(): ... pass # doing stuff When the ``do_stuff`` function will be called, the context manager will be used. This functionality is also available in the standard library of Python 3.2+ by using :class:`contextlib.ContextDecorator`, but here it is combined with all the other goodies given by :class:`ContextManager`. Another advantage that :class:`ContextManager` has over :class:`contextlib.ContextDecorator` is that it uses `Michele Simionato's excellent decorator module`_ to preserve the decorated function's signature. That's it. Inherit all your context managers from :class:`ContextManager` (or decorate your generator functions with :class:`ContextManagerType`) to enjoy all of these benefits. .. _here's a practical guide which explains how to use them.: http://effbot.org/zone/python-with-statement.htm .. _Michele Simionato's excellent decorator module: http://pypi.python.org/pypi/decorator