Skip to content

Commit 4a2f534

Browse files
Add context manager and try-finally video
1 parent d48f6ce commit 4a2f534

2 files changed

Lines changed: 388 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ James and his team are available for consulting, contracting, code reviews, and
1212

1313
| N | Code | Video |
1414
|-----| --- |--- |
15+
| 137 | [src](videos/137_context_managers) | [The ins and outs of context managers and try-finally in Python](https://youtu.be/LBJlGwJ899Y) |
1516
| 136 | [src](videos/136_python_debugging) | [Python Debugging (PyCharm + VS Code)](https://youtu.be/COa-JHYuW3M) |
1617
| 135 | [src](videos/135_modern_logging) | [Modern Python logging](https://youtu.be/9L77QExPmI0) |
1718
| 134 | no src | [Why Zero times Anything is Zero](https://youtu.be/R1gxkjTJcmE) |
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
import copy
2+
import decimal
3+
import os
4+
import socket
5+
import sys
6+
import tempfile
7+
import threading
8+
from contextlib import ExitStack, contextmanager, suppress
9+
10+
lock = threading.Lock()
11+
global_state = []
12+
13+
14+
def hello():
15+
with open(...) as fp:
16+
...
17+
18+
fp = open(...)
19+
try:
20+
...
21+
finally:
22+
fp.close()
23+
24+
25+
def non_cm_examples():
26+
fp = open("test.txt", "w")
27+
fp.write("Hello World")
28+
fp.close()
29+
30+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
31+
s.settimeout(1.0) # seconds
32+
s.connect(("mcoding.io", 80))
33+
s.close()
34+
35+
fp = tempfile.NamedTemporaryFile()
36+
fp.write(b"Hello World")
37+
fp.close()
38+
39+
lock.acquire()
40+
global_state.append(42)
41+
lock.release()
42+
43+
44+
def cm_examples():
45+
with open("test.txt", "w") as fp:
46+
fp.write("Hello World")
47+
48+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
49+
s.settimeout(1.0) # seconds
50+
s.connect(("mcoding.io", 80))
51+
52+
with tempfile.TemporaryFile() as fp:
53+
fp.write(b"Hello World")
54+
55+
with lock:
56+
global_state.append(42)
57+
58+
59+
def cm_multi_example():
60+
with open("test.txt") as fp:
61+
with open("test2.txt", "w") as fp2:
62+
fp2.write(fp.read())
63+
64+
with open("test.txt") as fp, open("test2.txt", "w") as fp2:
65+
fp2.write(fp.read())
66+
67+
68+
def exit_stack_example():
69+
filenames = [f"test{n}.txt" for n in range(3)]
70+
with ExitStack() as stack:
71+
fps = [stack.enter_context(open(filename, "w")) for filename in filenames]
72+
...
73+
74+
75+
def file_example_no_cm():
76+
fp = open("test.txt", "w")
77+
fp.write("Hello World")
78+
fp.close()
79+
80+
81+
def file_example_no_cm_early_return(early_return_condition):
82+
fp = open("test.txt", "w")
83+
if early_return_condition:
84+
return
85+
... # many lines later
86+
fp.close()
87+
88+
89+
def file_example_cm():
90+
with open("test.txt", "w") as fp:
91+
fp.write("Hello World")
92+
93+
94+
def add_print_to_close(obj):
95+
old_close = obj.close
96+
97+
def new_close():
98+
print("closing")
99+
old_close()
100+
101+
obj.close = new_close
102+
return obj
103+
104+
105+
def file_example_cm():
106+
fp = open("test.txt", "w")
107+
fp = add_print_to_close(fp)
108+
109+
print("before with")
110+
with fp:
111+
print("inside with")
112+
fp.write("Hello World")
113+
print("after with")
114+
115+
116+
def file_example_cm_with_exc():
117+
fp = open("test.txt", "w")
118+
fp = add_print_to_close(fp)
119+
120+
try:
121+
print("before with")
122+
with fp:
123+
print("inside with")
124+
raise ValueError("bad")
125+
print("after with")
126+
except ValueError as exc:
127+
print("caught exception", exc)
128+
129+
130+
def file_example_cm_with_return():
131+
def inner():
132+
fp = open("test.txt", "w")
133+
fp = add_print_to_close(fp)
134+
135+
print("before with")
136+
with fp:
137+
print("inside with")
138+
return print("returning!")
139+
print("after with")
140+
141+
inner()
142+
print("after return")
143+
144+
145+
def file_example_try_finally():
146+
fp = open("test.txt", "w")
147+
try:
148+
fp.write("Hello World")
149+
finally:
150+
fp.close()
151+
152+
153+
def file_example_try_except_finally():
154+
fp = open("test.txt", "w")
155+
print("before try")
156+
try:
157+
raise ValueError("bad")
158+
except ValueError as exc:
159+
print("caught exception", exc)
160+
finally:
161+
print("closing")
162+
fp.close()
163+
print("after try")
164+
165+
166+
def file_example_try_finally_interrupt():
167+
fp = open("test.txt", "w")
168+
try:
169+
return "inside"
170+
finally:
171+
fp.close()
172+
return "finally"
173+
174+
175+
def try_finally_infinite_loop():
176+
while True:
177+
print("loop")
178+
try:
179+
return "inside"
180+
finally:
181+
continue
182+
183+
184+
def file_example_try_finally_lost_exc():
185+
fp = open("test.txt", "w")
186+
try:
187+
raise ValueError("bad")
188+
finally:
189+
fp.close()
190+
return # exception is now gone
191+
192+
193+
def with_statement_semantics():
194+
with open("test.txt", "w") as fp:
195+
fp.write("Hello World")
196+
197+
# same as
198+
manager = open("test.txt", "w")
199+
enter = type(manager).__enter__
200+
exit = type(manager).__exit__
201+
value = enter(manager)
202+
hit_except = False
203+
204+
try:
205+
fp = value
206+
fp.write("Hello World")
207+
except:
208+
hit_except = True
209+
if not exit(manager, *sys.exc_info()):
210+
raise
211+
finally:
212+
if not hit_except:
213+
exit(manager, None, None, None)
214+
215+
216+
def try_finally_vs_with_statement():
217+
fp = open("test.txt", "w")
218+
try:
219+
fp.write("Hello World")
220+
finally:
221+
fp.close()
222+
223+
with open("test.txt", "w") as fp:
224+
fp.write("Hello World")
225+
226+
227+
class MyContextManager:
228+
def __enter__(self):
229+
return self
230+
231+
def __exit__(self, exc_type, exc_val, exc_tb):
232+
...
233+
234+
235+
def my_cm_example():
236+
with MyContextManager() as cm:
237+
raise ValueError
238+
239+
240+
def suppress_example():
241+
# remove it if it exists,
242+
# otherwise I don't care
243+
244+
with suppress(OSError):
245+
os.remove("test.txt")
246+
247+
248+
def _api_begin(arg):
249+
print(f"api begin: {arg}")
250+
return {"status": "ok"}
251+
252+
253+
def _api_end():
254+
print("api end")
255+
256+
257+
def api_begin(arg):
258+
val = _api_begin(arg)
259+
return _ExternalAPIContextManager(val)
260+
261+
262+
class _ExternalAPIContextManager:
263+
def __init__(self, val):
264+
self.val = val
265+
266+
def __enter__(self):
267+
return self.val
268+
269+
def __exit__(self, exc_type, exc_val, exc_tb):
270+
_api_end()
271+
272+
273+
def external_api_example():
274+
val = _api_begin("arg")
275+
print(f"use val: {val}")
276+
_api_end()
277+
278+
# instead
279+
with api_begin("arg") as val:
280+
print(f"use val: {val}")
281+
282+
283+
@contextmanager
284+
def api_begin2(arg):
285+
val = _api_begin(arg)
286+
yield val
287+
_api_end()
288+
289+
290+
@contextmanager
291+
def api_begin3(arg):
292+
val = _api_begin(arg)
293+
try:
294+
yield val
295+
finally:
296+
_api_end()
297+
298+
299+
class ActualContextManager:
300+
def __enter__(self):
301+
global global_state
302+
self.saved = global_state
303+
global_state = copy.deepcopy(global_state)
304+
# mutate copy if desired
305+
return global_state
306+
307+
def __exit__(self, exc_type, exc_val, exc_tb):
308+
global global_state
309+
global_state = self.saved
310+
self.saved = None
311+
312+
313+
def pi():
314+
# https://docs.python.org/3/library/decimal.html#module-decimal
315+
decimal.getcontext().prec += 2
316+
three = decimal.Decimal(3)
317+
lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
318+
while s != lasts:
319+
lasts = s
320+
n, na = n + na, na + 8
321+
d, da = d + da, da + 32
322+
t = (t * n) / d
323+
s += t
324+
decimal.getcontext().prec -= 2
325+
return +s
326+
327+
328+
def decimal_example():
329+
print(pi())
330+
331+
with decimal.localcontext(prec=100):
332+
print(pi())
333+
334+
335+
def nice_exit_example():
336+
try:
337+
sys.exit(1)
338+
finally:
339+
print("printing on the way out...")
340+
341+
342+
def not_nice_exit_example():
343+
try:
344+
os._exit(1)
345+
finally:
346+
print("this does not print")
347+
348+
349+
class UnluckyContextManager:
350+
def __enter__(self):
351+
lock.acquire()
352+
353+
def __exit__(self, exc_type, exc_val, exc_tb):
354+
lock.release()
355+
356+
357+
def unlucky_context_manager_example():
358+
try:
359+
while True: # interrupt me!
360+
with lock:
361+
pass
362+
finally:
363+
print(f"lock released on exit? {not lock.locked()}")
364+
365+
366+
def main():
367+
# cm_examples()
368+
# non_cm_examples()
369+
# cm_multi_example()
370+
# file_example_cm()
371+
# file_example_cm_with_exc()
372+
# file_example_cm_with_return()
373+
# file_example_try_finally_lost_exc()
374+
# try_finally_infinite_loop()
375+
# with_statement_semantics()
376+
# try_finally_vs_with_statement()
377+
# my_cm_example()
378+
# suppress_example()
379+
# external_api_example()
380+
# decimal_example()
381+
# nice_exit_example()
382+
# not_nice_exit_example()
383+
unlucky_context_manager_example()
384+
385+
386+
if __name__ == "__main__":
387+
main()

0 commit comments

Comments
 (0)