-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathContextManagers.html
More file actions
393 lines (372 loc) · 35 KB
/
ContextManagers.html
File metadata and controls
393 lines (372 loc) · 35 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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
<!DOCTYPE html>
<html class="writer-html5" lang="en" >
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Context Managers — Programming in Python 7.0 documentation</title>
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<!--[if lt IE 9]>
<script src="../_static/js/html5shiv.min.js"></script>
<![endif]-->
<script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/js/theme.js"></script>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="A Couple Handy Context Managers" href="../exercises/context-managers-exercise.html" />
<link rel="prev" title="Mailroom – Decoratoring it" href="../exercises/mailroom/mailroom-decorator.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" style="background: #4b2e83" >
<a href="../index.html">
<img src="../_static/UWPCE_logo_full.png" class="logo" alt="Logo"/>
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Topics in the Program</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../topics/01-setting_up/index.html">1. Setting up your Environment</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/02-basic_python/index.html">2. Basic Python</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/03-recursion_booleans/index.html">3. Booleans and Recursion</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/04-sequences_iteration/index.html">4. Sequences and Iteration</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/05-text_handling/index.html">5. Basic Text Handling</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/06-exceptions/index.html">6. Exception Handling</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/07-unit_testing/index.html">7. Unit Testing</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/08-dicts_sets/index.html">8. Dictionaries and Sets</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/09-files/index.html">9. File Handling</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/10-modules_packages/index.html">10. Modules and Packages</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/11-argument_passing/index.html">11. Advanced Argument Passing</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/12-comprehensions/index.html">12. Comprehensions</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/13-intro_oo/index.html">13. Intro to Object Oriented Programing</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/14-magic_methods/index.html">14. Properties and Magic Methods</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/15-subclassing/index.html">15. Subclassing and Inheritance</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/16-multiple_inheritance/index.html">16. Multiple Inheritance</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/17-functional_programming/index.html">17. Introduction to Functional Programming</a></li>
<li class="toctree-l1"><a class="reference internal" href="../topics/18-advanced_testing/index.html">18. Advanced Testing</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="../topics/99-extras/index.html">19. Extra Topics</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="Pep8.html">Coding Style and Linting</a></li>
<li class="toctree-l2"><a class="reference internal" href="CodeReviews.html">Code Reviews</a></li>
<li class="toctree-l2"><a class="reference internal" href="PersistanceAndSerialization.html">Persistence and Serialization</a></li>
<li class="toctree-l2"><a class="reference internal" href="Unicode.html">Unicode in Python</a></li>
<li class="toctree-l2"><a class="reference internal" href="IteratorsAndGenerators.html">Iterators and Generators</a></li>
<li class="toctree-l2"><a class="reference internal" href="Decorators.html">Decorators</a></li>
<li class="toctree-l2"><a class="reference internal" href="../exercises/mailroom/mailroom-decorator.html">Mailroom – Decoratoring it</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">Context Managers</a></li>
<li class="toctree-l2"><a class="reference internal" href="../exercises/context-managers-exercise.html">A Couple Handy Context Managers</a></li>
<li class="toctree-l2"><a class="reference internal" href="MetaProgramming.html">Metaprogramming</a></li>
<li class="toctree-l2"><a class="reference internal" href="../exercises/mailroom/mailroom-meta.html">Mailroom – metaprogramming it!</a></li>
<li class="toctree-l2"><a class="reference internal" href="Logging.html">Logging and the logging module</a></li>
<li class="toctree-l2"><a class="reference internal" href="Debugging.html">Debugging</a></li>
<li class="toctree-l2"><a class="reference internal" href="NoSQL.html">No SQL Databases</a></li>
<li class="toctree-l2"><a class="reference internal" href="GraphDatabases.html">Graph Databases</a></li>
<li class="toctree-l2"><a class="reference internal" href="Concurrency.html">Concurrent Programming</a></li>
<li class="toctree-l2"><a class="reference internal" href="Async.html">Asychronous Programming</a></li>
<li class="toctree-l2"><a class="reference internal" href="Coroutines.html">Notes on Coroutines</a></li>
<li class="toctree-l2"><a class="reference internal" href="ThreadingMultiprocessing.html">Threading and multiprocessing</a></li>
<li class="toctree-l2"><a class="reference internal" href="../exercises/threaded_downloader.html">Threaded Web Scraper</a></li>
<li class="toctree-l2"><a class="reference internal" href="Profiling.html">Performance and Profiling</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" style="background: #4b2e83" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">Programming in Python</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content style-external-links">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html" class="icon icon-home"></a> »</li>
<li><a href="../topics/99-extras/index.html"><span class="section-number">19. </span>Extra Topics</a> »</li>
<li>Context Managers</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/modules/ContextManagers.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul><div class="rst-breadcrumbs-buttons" role="navigation" aria-label="Sequential page navigation">
<a href="../exercises/mailroom/mailroom-decorator.html" class="btn btn-neutral float-left" title="Mailroom – Decoratoring it" accesskey="p"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
<a href="../exercises/context-managers-exercise.html" class="btn btn-neutral float-right" title="A Couple Handy Context Managers" accesskey="n">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="context-managers">
<span id="id1"></span><h1>Context Managers<a class="headerlink" href="#context-managers" title="Permalink to this headline"></a></h1>
<p>You’ve seen the <code class="docutils literal notranslate"><span class="pre">with</span></code> statement – probably used for working with files. Now you’ll learn what that’s all about.</p>
<div class="section" id="managing-resources">
<h2>Managing Resources<a class="headerlink" href="#managing-resources" title="Permalink to this headline"></a></h2>
<p><strong>Repetition in code stinks (DRY!)</strong></p>
<p>A large source of repetition in code deals with the handling of external
resources.</p>
<p>As an example, how many times do you think you might type something like the following code:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">file_handle</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'filename.txt'</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">)</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="n">file_handle</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">file_handle</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="c1"># do some stuff with the contents</span>
</pre></div>
</div>
<p>Not only is this a couple extra lines of code to write, it’s also prone to error:</p>
<p>What happens if you forget to call <code class="docutils literal notranslate"><span class="pre">.close()</span></code>?</p>
<p>What happens if reading the file raises an exception?</p>
<p>So you really should write it something like:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="n">file_handle</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="n">file_handle</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">IOError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"The file couldn't be opened"</span><span class="p">)</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">file_handle</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
</div>
<p>And that is getting pretty ugly, and hard to get right.</p>
<div class="section" id="handling-general-resources">
<h3>Handling General Resources<a class="headerlink" href="#handling-general-resources" title="Permalink to this headline"></a></h3>
<p>Leaving an open file handle laying around is bad enough. What if the resource
is a network connection, or a database cursor?</p>
<p>Starting in version 2.5, Python provides a structure for reducing the
repetition needed to handle resources like this.</p>
<p><strong>Context Managers</strong></p>
<p>You can encapsulate the setup, error handling, and teardown of resources in a
few simple steps.</p>
<p>The key is to use the <code class="docutils literal notranslate"><span class="pre">with</span></code> statement.</p>
</div>
<div class="section" id="with-a-little-help">
<h3><code class="docutils literal notranslate"><span class="pre">with</span></code> a little help<a class="headerlink" href="#with-a-little-help" title="Permalink to this headline"></a></h3>
<p>Since the introduction of the <code class="docutils literal notranslate"><span class="pre">with</span></code> statement in <a class="reference external" href="https://www.python.org/dev/peps/pep-0343/">pep343</a>, the above seven lines of defensive code have been replaced with this simple form:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'filename'</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="n">file_handle</span><span class="p">:</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="n">file_handle</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="c1"># do something with file_content</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">open</span></code> builtin is defined as a <em>context manager</em>.</p>
<p>The resource it returns (<code class="docutils literal notranslate"><span class="pre">file_handle</span></code>) is automatically and reliably closed
when the code block ends.</p>
<p>At this point in Python history, many functions you might expect to behave this way do:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">open</span></code> works as a context manager.</p></li>
<li><p>network connections via <code class="docutils literal notranslate"><span class="pre">socket</span></code> do as well.</p></li>
<li><p>most implementations of database wrappers can open connections or cursors as
context managers.</p></li>
<li><p>…</p></li>
<li><p>But what if you are working with a library that doesn’t support this
(<code class="docutils literal notranslate"><span class="pre">urllib</span></code>)?</p></li>
</ul>
</div>
<div class="section" id="close-it-automatically">
<h3>Close It Automatically<a class="headerlink" href="#close-it-automatically" title="Permalink to this headline"></a></h3>
<p>There are a couple of ways you can go.</p>
<p>If the resource in questions has a <code class="docutils literal notranslate"><span class="pre">.close()</span></code> method, then you can simply use the <code class="docutils literal notranslate"><span class="pre">closing</span></code> context manager from <code class="docutils literal notranslate"><span class="pre">contextlib</span></code> to handle the issue:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">urllib</span> <span class="kn">import</span> <span class="n">request</span>
<span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">closing</span>
<span class="k">with</span> <span class="n">closing</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="s1">'http://google.com'</span><span class="p">))</span> <span class="k">as</span> <span class="n">web_connection</span><span class="p">:</span>
<span class="c1"># do something with the open resource</span>
<span class="c1"># and here, it will be closed automatically</span>
</pre></div>
</div>
<p>But what if the thing doesn’t have a <code class="docutils literal notranslate"><span class="pre">close()</span></code> method, or you’re creating
the thing and it shouldn’t have a <code class="docutils literal notranslate"><span class="pre">close()</span></code> method?</p>
<p>(full confession: <code class="docutils literal notranslate"><span class="pre">urlib.request</span></code> was not a context manager in py2 – but it is in py3 – but the issue still comes up with third-party packages and your own code!)</p>
</div>
<div class="section" id="do-it-yourself">
<h3>Do It Yourself<a class="headerlink" href="#do-it-yourself" title="Permalink to this headline"></a></h3>
<p>If you do need to support resource management of some sort, you can create a context manager of your own with the context manager protocol.</p>
<p>The interface is simple. It must be a class that implements two
more of the nifty python <em>special methods</em></p>
<dl class="simple">
<dt><code class="docutils literal notranslate"><span class="pre">__enter__(self)</span></code>:</dt><dd><p>Called when the <code class="docutils literal notranslate"><span class="pre">with</span></code> statement is run, it should return something to work with in the created context.</p>
</dd>
<dt><code class="docutils literal notranslate"><span class="pre">__exit__(self,</span> <span class="pre">e_type,</span> <span class="pre">e_val,</span> <span class="pre">e_traceback)</span></code>:</dt><dd><p>Clean-up that needs to happen is implemented here.</p>
</dd>
</dl>
<p>The arguments will be the exception raised in the context.</p>
<p>If the exception will be handled here, return <code class="docutils literal notranslate"><span class="pre">True</span></code>. If not, return <code class="docutils literal notranslate"><span class="pre">False</span></code>.</p>
<p>Let’s see this in action to get a sense of what happens.</p>
</div>
<div class="section" id="an-example">
<h3>An Example<a class="headerlink" href="#an-example" title="Permalink to this headline"></a></h3>
<p>Consider this code:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Context</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="sd">"""from Doug Hellmann, PyMOTW</span>
<span class="sd"> https://pymotw.com/3/contextlib/#module-contextlib</span>
<span class="sd"> """</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">handle_error</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'__init__(</span><span class="si">{}</span><span class="s1">)'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">handle_error</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">handle_error</span> <span class="o">=</span> <span class="n">handle_error</span>
<span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'__enter__()'</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span>
<span class="k">def</span> <span class="fm">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exc_type</span><span class="p">,</span> <span class="n">exc_val</span><span class="p">,</span> <span class="n">exc_tb</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'__exit__(</span><span class="si">{}</span><span class="s1">, </span><span class="si">{}</span><span class="s1">, </span><span class="si">{}</span><span class="s1">)'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">exc_type</span><span class="p">,</span> <span class="n">exc_val</span><span class="p">,</span> <span class="n">exc_tb</span><span class="p">))</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle_error</span>
</pre></div>
</div>
<p><a class="reference download internal" download="" href="../_downloads/1cd83ce40e4950c45c2c6377ed05f15b/context_manager.py"><code class="xref download docutils literal notranslate"><span class="pre">context_manager.py</span></code></a></p>
<p>This class doesn’t do much of anything, but playing with it can help
clarify the order in which things happen:</p>
<div class="highlight-ipython notranslate"><div class="highlight"><pre><span></span><span class="gp">In [46]: </span><span class="k">with</span> <span class="n">Context</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span> <span class="k">as</span> <span class="n">foo</span><span class="p">:</span>
<span class="go"> ....: print('This is in the context')</span>
<span class="go"> ....: raise RuntimeError('this is the error message')</span>
<span class="go"> ....:</span>
<span class="go">__init__(True)</span>
<span class="go">__enter__()</span>
<span class="go">This is in the context</span>
<span class="go">__exit__(<class 'RuntimeError'>, this is the error message,</span>
<span class="go"> <traceback object at 0x1047873c8>)</span>
</pre></div>
</div>
<p>Because the <code class="docutils literal notranslate"><span class="pre">__exit__</span></code> method returns <code class="docutils literal notranslate"><span class="pre">True</span></code>, the raised error is ‘handled’.</p>
<p>What if we try with <code class="docutils literal notranslate"><span class="pre">False</span></code>?</p>
<div class="highlight-ipython notranslate"><div class="highlight"><pre><span></span><span class="gp">In [3]: </span><span class="k">with</span> <span class="n">Context</span><span class="p">(</span><span class="kc">False</span><span class="p">)</span> <span class="k">as</span> <span class="n">foo</span><span class="p">:</span>
<span class="gp"> ...: </span> <span class="nb">print</span><span class="p">(</span><span class="s2">"this is in the context"</span><span class="p">)</span>
<span class="gp"> ...: </span> <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s1">'this is the error message'</span><span class="p">)</span>
<span class="gp"> ...:</span>
<span class="go">__init__(False)</span>
<span class="go">__enter__()</span>
<span class="go">this is in the context</span>
<span class="go">__exit__(<class 'RuntimeError'>, this is the error message, <traceback object at 0x10349e888>)</span>
<span class="gt">---------------------------------------------------------------------------</span>
<span class="ne">RuntimeError</span><span class="g g-Whitespace"> </span>Traceback (most recent call last)
<span class="nn"><ipython-input-3-8837b3d7f123></span> in <span class="ni"><module></span><span class="nt">()</span>
<span class="g g-Whitespace"> </span><span class="mi">1</span> <span class="k">with</span> <span class="n">Context</span><span class="p">(</span><span class="kc">False</span><span class="p">)</span> <span class="k">as</span> <span class="n">foo</span><span class="p">:</span>
<span class="g g-Whitespace"> </span><span class="mi">2</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"this is in the context"</span><span class="p">)</span>
<span class="ne">----> </span><span class="mi">3</span> <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s1">'this is the error message'</span><span class="p">)</span>
<span class="ne">RuntimeError</span>: this is the error message
</pre></div>
</div>
<p>So this time, the context manager did not catch the error – so it was raised in the usual way.</p>
</div>
<div class="section" id="the-parameters-to-exit">
<h3>The parameters to <code class="docutils literal notranslate"><span class="pre">__exit__</span></code><a class="headerlink" href="#the-parameters-to-exit" title="Permalink to this headline"></a></h3>
<p>In real life, a context manager could have pretty much any error raised in its context. And the context manager will likely only be able to “properly” handle particular Exceptions.</p>
<p>So the <code class="docutils literal notranslate"><span class="pre">__exit__</span></code> method takes all the information about the exception as parameters:</p>
<p><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">__exit__(self,</span> <span class="pre">exc_type,</span> <span class="pre">exc_val,</span> <span class="pre">exc_tb)</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">exc_type</span></code>: the type of the Exception</p>
<p><code class="docutils literal notranslate"><span class="pre">exc_val</span></code>: the value of the Exception</p>
<p><code class="docutils literal notranslate"><span class="pre">exc_tb</span></code>: the Exception Traceback object</p>
<p>The type lets you check if this is a type you know how to handle:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">exc_type</span> <span class="ow">is</span> <span class="ne">RuntimeError</span><span class="p">:</span>
</pre></div>
</div>
<p>The value is the exception object itself.</p>
<p>And the traceback is a full traceback object. Traceback objects hold all the information about the context in which an error occurred. It’s pretty advanced stuff, so you can mostly ignore it, but if you want to know more, there are tools for working with them in the <code class="docutils literal notranslate"><span class="pre">traceback</span></code> module.</p>
<p><a class="reference external" href="https://docs.python.org/3/library/traceback.html">https://docs.python.org/3/library/traceback.html</a></p>
</div>
<div class="section" id="the-contextmanager-decorator">
<h3>The <code class="docutils literal notranslate"><span class="pre">contextmanager</span></code> decorator<a class="headerlink" href="#the-contextmanager-decorator" title="Permalink to this headline"></a></h3>
<p>Similar to writing iterable classes, there’s a fair bit of bookkeeping involved. It turns out you can take advantage of generator functions to do that bookkeeping for you.</p>
<p><code class="docutils literal notranslate"><span class="pre">contextlib.contextmanager</span></code> decorator will turn a generator function into context manager.</p>
<p>Consider this code:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">contextmanager</span>
<span class="nd">@contextmanager</span>
<span class="k">def</span> <span class="nf">context</span><span class="p">(</span><span class="n">boolean</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"__init__ code here"</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"__enter__ code goes here"</span><span class="p">)</span>
<span class="k">yield</span> <span class="nb">object</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"errors handled here"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">boolean</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">finally</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"__exit__ cleanup goes here"</span><span class="p">)</span>
</pre></div>
</div>
<p>The code is similar to the class defined previously.</p>
<p>And using it has similar results. We can handle errors:</p>
<div class="highlight-ipython notranslate"><div class="highlight"><pre><span></span><span class="gp">In [96]: </span><span class="k">with</span> <span class="n">context</span><span class="p">(</span><span class="kc">True</span><span class="p">):</span>
<span class="gp"> ....: </span> <span class="nb">print</span><span class="p">(</span><span class="s2">"in the context"</span><span class="p">)</span>
<span class="gp"> ....: </span> <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">"error raised"</span><span class="p">)</span>
<span class="gp"> ....:</span>
<span class="go">__init__ code here</span>
<span class="go">__enter__ code goes here</span>
<span class="go">in the context</span>
<span class="go">errors handled here</span>
<span class="go">__exit__ cleanup goes here</span>
</pre></div>
</div>
<p>Or, we can allow them to propagate:</p>
<div class="highlight-ipython notranslate"><div class="highlight"><pre><span></span><span class="gp">In [51]: </span><span class="k">with</span> <span class="n">context</span><span class="p">(</span><span class="kc">False</span><span class="p">):</span>
<span class="gp"> ....: </span> <span class="nb">print</span><span class="p">(</span><span class="s2">"in the context"</span><span class="p">)</span>
<span class="gp"> ....: </span> <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">"error raised"</span><span class="p">)</span>
<span class="go">__init__ code here</span>
<span class="go">__enter__ code goes here</span>
<span class="go">in the context</span>
<span class="go">errors handled here</span>
<span class="go">__exit__ cleanup goes here</span>
<span class="gt">---------------------------------------------------------------------------</span>
<span class="ne">RuntimeError</span><span class="g g-Whitespace"> </span>Traceback (most recent call last)
<span class="nn"><ipython-input-51-641528ffa695></span> in <span class="ni"><module></span><span class="nt">()</span>
<span class="g g-Whitespace"> </span><span class="mi">1</span> <span class="k">with</span> <span class="n">context</span><span class="p">(</span><span class="kc">False</span><span class="p">):</span>
<span class="g g-Whitespace"> </span><span class="mi">2</span> <span class="nb">print</span> <span class="s2">"in the context"</span>
<span class="ne">----> </span><span class="mi">3</span> <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">"error raised"</span><span class="p">)</span>
<span class="g g-Whitespace"> </span><span class="mi">4</span>
<span class="ne">RuntimeError</span>: error raised
</pre></div>
</div>
</div>
<div class="section" id="mixing-context-managers-with-generators">
<h3>Mixing context_managers with generators<a class="headerlink" href="#mixing-context-managers-with-generators" title="Permalink to this headline"></a></h3>
<p>You can put a <code class="docutils literal notranslate"><span class="pre">yield</span></code> inside a context manager as well.</p>
<p>here is a generator function that gives yields all the files in a directory:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pathlib</span>
<span class="k">def</span> <span class="nf">file_yielder</span><span class="p">(</span><span class="nb">dir</span><span class="o">=</span><span class="s2">"."</span><span class="p">,</span> <span class="n">pattern</span><span class="o">=</span><span class="s2">"*"</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> iterate over all the files that match the pattern</span>
<span class="sd"> pattern use a "glob" pattern, like: *.py</span>
<span class="sd"> """</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="nb">dir</span><span class="p">)</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">pattern</span><span class="p">):</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">as</span> <span class="n">file_obj</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">file_obj</span>
</pre></div>
</div>
<p><a class="reference download internal" download="" href="../_downloads/d296af27e123626bc6a13f14a2f2a942/file_yielder.py"><code class="xref download docutils literal notranslate"><span class="pre">file_yielder.py</span></code></a></p>
<p>So the <code class="docutils literal notranslate"><span class="pre">yield</span></code> is inside the file context manager, so that state will be preserved while the file object is in use.</p>
<p>This generator can be used like so:</p>
<div class="highlight-ipython notranslate"><div class="highlight"><pre><span></span><span class="gp">In [20]: </span><span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">file_yielder</span><span class="p">(</span><span class="n">pattern</span><span class="o">=</span><span class="s2">"*.py"</span><span class="p">):</span>
<span class="go"> ...: print("The first line of: {} is:\n{}".format(f.name, f.readline()))</span>
</pre></div>
</div>
<p>Each iteration through the loop, the previous file gets closed, and the new one opened. If there is an exception raised inside that loop, the last file will get properly closed.</p>
</div>
</div>
</div>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="../exercises/mailroom/mailroom-decorator.html" class="btn btn-neutral float-left" title="Mailroom – Decoratoring it" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
<a href="../exercises/context-managers-exercise.html" class="btn btn-neutral float-right" title="A Couple Handy Context Managers" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>© Copyright 2020, University of Washington, Natasha Aleksandrova, Christopher Barker, Brian Dorsey, Cris Ewing, Christy Heaton, Jon Jacky, Maria McKinley, Andy Miles, Rick Riehle, Joseph Schilz, Joseph Sheedy, Hosung Song. Creative Commons Attribution-ShareAlike 4.0 license.</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>