forked from cool-RR/python_toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontext-management.txt
More file actions
147 lines (108 loc) · 6.11 KB
/
context-management.txt
File metadata and controls
147 lines (108 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
..
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