Skip to content

Commit 34c75d9

Browse files
committed
-
1 parent b7ff481 commit 34c75d9

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

docs/topics/context-managers.txt

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,130 @@
88

99
:mod:`context_managers` - documentation not written
1010
======================================
11+
12+
Context managers are awesome
13+
----------------------------
14+
15+
I love context managers, and I love the :keyword:`with` keyword. If you've
16+
never dealt with context managers or :keyword:`with`, `here's a practical guide
17+
which explains how to use them.`_ You may also read the more official :pep:`PEP
18+
343 <343>` which introduced these features to the language.
19+
20+
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.
21+
22+
Now, you don't *need* any official :class:`ContextManager` class in order to
23+
use context managers or define them; you just need to define
24+
:method:`__enter__` and :method:`__exit__` methods in your class, and then you
25+
can use your class as a context manager. *But*, if you use the
26+
:class:`ContextManager` class as a base class to your context manager class,
27+
you could enjoy a few more features that might make your code a bit more
28+
concise and elegant.
29+
30+
What does :class:`ContextManager` add?
31+
--------------------------------------
32+
33+
The :class:`ContextManager` class allows using context managers as decorators
34+
(in addition to their normal use) and supports writing context managers in a
35+
new form called :method:`manage_context`. (As well as the original forms).
36+
First let's import:
37+
38+
>>> from python_toolbox import context_managers
39+
40+
Now let's go over the features one by one.
41+
42+
The :class:`ContextManager` class allows you to **define** context managers in
43+
new ways and to **use **context managers in new ways. I'll explain both of
44+
these; let's start with **defining** context managers.
45+
46+
Defining context managers
47+
-------------------------
48+
49+
There are 3 different ways in which context managers can be defined, and each
50+
has their own advantages and disadvantages over the others.
51+
52+
- The classic way to define a context manager is to define a class with
53+
:method:`__enter__` and :method:`__exit__` methods. This is allowed, and if you
54+
do this you should still inherit from :class:`ContextManager`. Example:
55+
56+
>>> class MyContextManager(context_managers.ContextManager):
57+
... def __enter__(self):
58+
... pass # preparation
59+
... def __exit__(self, type_=None, value=None, traceback=None):
60+
... pass # cleanup
61+
62+
- As a decorated generator, like so:
63+
64+
>>> @context_managers.ContextManagerType
65+
... def MyContextManager():
66+
... # preparation
67+
... try:
68+
... yield
69+
... finally:
70+
... pass # cleanup
71+
72+
73+
The advantage of this approach is its brevity, and it may be a good fit for
74+
relatively simple context managers that don't require defining an actual class.
75+
This usage is nothing new; it's also available when using the standard
76+
library's :decorator:`contextlib.contextmanager` decorator. One thing that is
77+
allowed here that :mod:`contextlib` doesn't allow is to yield the context
78+
manager itself by doing ``yield context_managers.SelfHook``.
79+
80+
- The third and novel way is by defining a class with a :method:`manage_context`
81+
method which returns a decorator. Example:
82+
83+
>>> class MyContextManager(ContextManager):
84+
... def manage_context(self):
85+
... do_some_preparation()
86+
... with other_context_manager:
87+
... yield self
88+
89+
90+
This approach is sometimes cleaner than defining :method:`__enter__` and
91+
:method:`__exit__`; especially when using another context manager inside
92+
:method:`manage_context`. In our example we did ``with other_context_manager``
93+
in our :method:`manage_context`, which is shorter, more idiomatic and less
94+
double-underscore-y than the equivalent classic definition:
95+
96+
>>> class MyContextManager(object):
97+
... def __enter__(self):
98+
... do_some_preparation()
99+
... other_context_manager.__enter__()
100+
... return self
101+
... def __exit__(self, *exc):
102+
... return other_context_manager.__exit__(*exc)
103+
104+
Another advantage of the :method:`manage_context` approach over
105+
:method:`__enter__` and :method:`__exit__` is that it's better at handling
106+
exceptions, since any exceptions would be raised inside
107+
:method:`manage_context` where we could :keyword:`except` them, which is much
108+
more idiomatic than the way :method:`__exit__` handles exceptions, which is by
109+
receiving their type and returning whether to swallow them or not.
110+
111+
These were the different ways of defining a context manager. Now let's see the different ways of **using** a context manager:
112+
113+
Using context managers
114+
----------------------
115+
116+
117+
There are 2 different ways in which context managers can be used:
118+
<ol><li>The plain old honest-to-Guido :keyword:`with` keyword:
119+
<pre>with MyContextManager() as my_context_manager:
120+
do_stuff()
121+
</pre>
122+
</li>
123+
<li>
124+
As a decorator to a function:
125+
<pre>@MyContextManager()
126+
def do_stuff():
127+
pass # doing stuff
128+
</pre>
129+
When the <code>do_stuff</code> 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 <code><a href="http://docs.python.org/dev/library/contextlib.html#contextlib.ContextDecorator">contextlib.ContextDecorator</a></code>, but here it is combined with all the other goodies given by :class:`ContextManager`. Another advantage that :class:`ContextManager` has over <code>ContextDecorator</code> is that it uses <a href="http://pypi.python.org/pypi/decorator">Michele Simionato's excellent decorator module</a> to preserve the decorated function's signature.
130+
</li>
131+
</ol>That's it. Inherit all your context managers from :class:`ContextManager` (or decorate your generator functions with <code>ContextManagerType</code>) to enjoy all of these benefits.
132+
<h2>Source and tests for <code>context_manager</code></h2>
133+
<a href="https://github.com/cool-RR/GarlicSim/tree/master/garlicsim/garlicsim/general_misc/context_manager.py">Source for <code>context_manager</code>, well-documented</a>. Here are <a href="https://github.com/cool-RR/GarlicSim/tree/master/garlicsim/test_garlicsim/test_general_misc/test_context_manager">the tests</a>.
134+
Yes, I used a double metaclass in the implementation, i.e. a metaclass which has a metaclass itself.
135+
There is also a <a href="https://github.com/cool-RR/GarlicSim-for-Python-3.x/tree/master/garlicsim_py3/garlicsim/general_misc/context_manager.py">Python 3 version of <code>context_manager</code></a>, and <a href="https://github.com/cool-RR/GarlicSim-for-Python-3.x/tree/master/garlicsim_py3/test_garlicsim/test_general_misc/test_context_manager">here are its tests</a>. It's available with the <a href="http://pypi.python.org/pypi/garlicsim_py3">Python 3 fork of GarlicSim</a>.
136+
137+
.. here's a practical guide which explains how to use them.: http://effbot.org/zone/python-with-statement.htm

0 commit comments

Comments
 (0)