blob: 5a080654acb6c78ee258453bc28c20921e5ac8d1 [file] [log] [blame]
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001"""Unit tests for contextlib.py, and other context managers."""
2
Raymond Hettinger088cbf22013-10-10 00:46:57 -07003import io
R. David Murray378c0cf2010-02-24 01:46:21 +00004import sys
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00005import tempfile
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02006import threading
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00007import unittest
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00008from contextlib import * # Tests __all__
Benjamin Petersonee8712c2008-05-20 21:35:26 +00009from test import support
Hai Shi96a6a6d2020-07-09 21:25:10 +080010from test.support import os_helper
Martin Teichmanndd0e0872018-01-28 05:17:46 +010011import weakref
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000012
Florent Xicluna41fe6152010-04-02 18:52:12 +000013
Brett Cannon9e080e02016-04-08 12:15:27 -070014class TestAbstractContextManager(unittest.TestCase):
15
16 def test_enter(self):
17 class DefaultEnter(AbstractContextManager):
18 def __exit__(self, *args):
19 super().__exit__(*args)
20
21 manager = DefaultEnter()
22 self.assertIs(manager.__enter__(), manager)
23
24 def test_exit_is_abstract(self):
25 class MissingExit(AbstractContextManager):
26 pass
27
28 with self.assertRaises(TypeError):
29 MissingExit()
30
31 def test_structural_subclassing(self):
32 class ManagerFromScratch:
33 def __enter__(self):
34 return self
35 def __exit__(self, exc_type, exc_value, traceback):
36 return None
37
38 self.assertTrue(issubclass(ManagerFromScratch, AbstractContextManager))
39
40 class DefaultEnter(AbstractContextManager):
41 def __exit__(self, *args):
42 super().__exit__(*args)
43
44 self.assertTrue(issubclass(DefaultEnter, AbstractContextManager))
45
Jelle Zijlstra57161aa2017-06-09 08:21:47 -070046 class NoEnter(ManagerFromScratch):
47 __enter__ = None
48
49 self.assertFalse(issubclass(NoEnter, AbstractContextManager))
50
51 class NoExit(ManagerFromScratch):
52 __exit__ = None
53
54 self.assertFalse(issubclass(NoExit, AbstractContextManager))
55
Brett Cannon9e080e02016-04-08 12:15:27 -070056
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000057class ContextManagerTestCase(unittest.TestCase):
58
59 def test_contextmanager_plain(self):
60 state = []
61 @contextmanager
62 def woohoo():
63 state.append(1)
64 yield 42
65 state.append(999)
66 with woohoo() as x:
67 self.assertEqual(state, [1])
68 self.assertEqual(x, 42)
69 state.append(x)
70 self.assertEqual(state, [1, 42, 999])
71
72 def test_contextmanager_finally(self):
73 state = []
74 @contextmanager
75 def woohoo():
76 state.append(1)
77 try:
78 yield 42
79 finally:
80 state.append(999)
Florent Xicluna41fe6152010-04-02 18:52:12 +000081 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000082 with woohoo() as x:
83 self.assertEqual(state, [1])
84 self.assertEqual(x, 42)
85 state.append(x)
86 raise ZeroDivisionError()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000087 self.assertEqual(state, [1, 42, 999])
88
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000089 def test_contextmanager_no_reraise(self):
90 @contextmanager
91 def whee():
92 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000093 ctx = whee()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000094 ctx.__enter__()
95 # Calling __exit__ should not result in an exception
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000096 self.assertFalse(ctx.__exit__(TypeError, TypeError("foo"), None))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000097
98 def test_contextmanager_trap_yield_after_throw(self):
99 @contextmanager
100 def whoo():
101 try:
102 yield
103 except:
104 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +0000105 ctx = whoo()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000106 ctx.__enter__()
107 self.assertRaises(
108 RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
109 )
110
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000111 def test_contextmanager_except(self):
112 state = []
113 @contextmanager
114 def woohoo():
115 state.append(1)
116 try:
117 yield 42
Guido van Rossumb940e112007-01-10 16:19:56 +0000118 except ZeroDivisionError as e:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000119 state.append(e.args[0])
120 self.assertEqual(state, [1, 42, 999])
121 with woohoo() as x:
122 self.assertEqual(state, [1])
123 self.assertEqual(x, 42)
124 state.append(x)
125 raise ZeroDivisionError(999)
126 self.assertEqual(state, [1, 42, 999])
127
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400128 def test_contextmanager_except_stopiter(self):
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400129 @contextmanager
130 def woohoo():
131 yield
Miss Islington (bot)68b46902021-07-20 12:12:47 -0700132
133 class StopIterationSubclass(StopIteration):
134 pass
135
136 for stop_exc in (StopIteration('spam'), StopIterationSubclass('spam')):
137 with self.subTest(type=type(stop_exc)):
138 try:
139 with woohoo():
140 raise stop_exc
141 except Exception as ex:
142 self.assertIs(ex, stop_exc)
143 else:
144 self.fail(f'{stop_exc} was suppressed')
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400145
146 def test_contextmanager_except_pep479(self):
147 code = """\
148from __future__ import generator_stop
149from contextlib import contextmanager
150@contextmanager
151def woohoo():
152 yield
153"""
154 locals = {}
155 exec(code, locals, locals)
156 woohoo = locals['woohoo']
157
158 stop_exc = StopIteration('spam')
159 try:
160 with woohoo():
161 raise stop_exc
162 except Exception as ex:
163 self.assertIs(ex, stop_exc)
164 else:
165 self.fail('StopIteration was suppressed')
166
svelankar00c75e92017-04-11 05:11:13 -0400167 def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
168 @contextmanager
169 def test_issue29692():
170 try:
171 yield
172 except Exception as exc:
173 raise RuntimeError('issue29692:Chained') from exc
174 try:
175 with test_issue29692():
176 raise ZeroDivisionError
177 except Exception as ex:
178 self.assertIs(type(ex), RuntimeError)
179 self.assertEqual(ex.args[0], 'issue29692:Chained')
180 self.assertIsInstance(ex.__cause__, ZeroDivisionError)
181
182 try:
183 with test_issue29692():
184 raise StopIteration('issue29692:Unchained')
185 except Exception as ex:
186 self.assertIs(type(ex), StopIteration)
187 self.assertEqual(ex.args[0], 'issue29692:Unchained')
188 self.assertIsNone(ex.__cause__)
189
R. David Murray378c0cf2010-02-24 01:46:21 +0000190 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000191 def attribs(**kw):
192 def decorate(func):
193 for k,v in kw.items():
194 setattr(func,k,v)
195 return func
196 return decorate
197 @contextmanager
198 @attribs(foo='bar')
199 def baz(spam):
200 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000201 return baz
202
203 def test_contextmanager_attribs(self):
204 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000205 self.assertEqual(baz.__name__,'baz')
206 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000207
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000208 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000209 def test_contextmanager_doc_attrib(self):
210 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000211 self.assertEqual(baz.__doc__, "Whee!")
212
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000213 @support.requires_docstrings
214 def test_instance_docstring_given_cm_docstring(self):
215 baz = self._create_contextmanager_attribs()(None)
216 self.assertEqual(baz.__doc__, "Whee!")
217
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300218 def test_keywords(self):
219 # Ensure no keyword arguments are inhibited
220 @contextmanager
221 def woohoo(self, func, args, kwds):
222 yield (self, func, args, kwds)
223 with woohoo(self=11, func=22, args=33, kwds=44) as target:
224 self.assertEqual(target, (11, 22, 33, 44))
225
Martin Teichmanndd0e0872018-01-28 05:17:46 +0100226 def test_nokeepref(self):
227 class A:
228 pass
229
230 @contextmanager
231 def woohoo(a, b):
232 a = weakref.ref(a)
233 b = weakref.ref(b)
Miss Islington (bot)0ea5e0d2021-07-26 14:21:36 -0700234 # Allow test to work with a non-refcounted GC
235 support.gc_collect()
Martin Teichmanndd0e0872018-01-28 05:17:46 +0100236 self.assertIsNone(a())
237 self.assertIsNone(b())
238 yield
239
240 with woohoo(A(), b=A()):
241 pass
242
243 def test_param_errors(self):
244 @contextmanager
245 def woohoo(a, *, b):
246 yield
247
248 with self.assertRaises(TypeError):
249 woohoo()
250 with self.assertRaises(TypeError):
251 woohoo(3, 5)
252 with self.assertRaises(TypeError):
253 woohoo(b=3)
254
255 def test_recursive(self):
256 depth = 0
257 @contextmanager
258 def woohoo():
259 nonlocal depth
260 before = depth
261 depth += 1
262 yield
263 depth -= 1
264 self.assertEqual(depth, before)
265
266 @woohoo()
267 def recursive():
268 if depth < 10:
269 recursive()
270
271 recursive()
272 self.assertEqual(depth, 0)
273
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000274
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000275class ClosingTestCase(unittest.TestCase):
276
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000277 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000278 def test_instance_docs(self):
279 # Issue 19330: ensure context manager instances have good docstrings
280 cm_docstring = closing.__doc__
281 obj = closing(None)
282 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000283
284 def test_closing(self):
285 state = []
286 class C:
287 def close(self):
288 state.append(1)
289 x = C()
290 self.assertEqual(state, [])
291 with closing(x) as y:
292 self.assertEqual(x, y)
293 self.assertEqual(state, [1])
294
295 def test_closing_error(self):
296 state = []
297 class C:
298 def close(self):
299 state.append(1)
300 x = C()
301 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000302 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000303 with closing(x) as y:
304 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000305 1 / 0
306 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000307
Jesse-Bakker0784a2e2017-11-23 01:23:28 +0100308
309class NullcontextTestCase(unittest.TestCase):
310 def test_nullcontext(self):
311 class C:
312 pass
313 c = C()
314 with nullcontext(c) as c_in:
315 self.assertIs(c_in, c)
316
317
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000318class FileContextTestCase(unittest.TestCase):
319
320 def testWithOpen(self):
321 tfn = tempfile.mktemp()
322 try:
323 f = None
Inada Naoki35715d12021-04-04 09:01:23 +0900324 with open(tfn, "w", encoding="utf-8") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000325 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000326 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000327 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000328 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000329 with self.assertRaises(ZeroDivisionError):
Inada Naoki35715d12021-04-04 09:01:23 +0900330 with open(tfn, "r", encoding="utf-8") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000331 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000332 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000333 1 / 0
334 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000335 finally:
Hai Shi96a6a6d2020-07-09 21:25:10 +0800336 os_helper.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000337
338class LockContextTestCase(unittest.TestCase):
339
340 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000341 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000342 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000343 self.assertTrue(locked())
344 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000345 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000346 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000347 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000348 1 / 0
349 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000350
351 def testWithLock(self):
352 lock = threading.Lock()
353 self.boilerPlate(lock, lock.locked)
354
355 def testWithRLock(self):
356 lock = threading.RLock()
357 self.boilerPlate(lock, lock._is_owned)
358
359 def testWithCondition(self):
360 lock = threading.Condition()
361 def locked():
362 return lock._is_owned()
363 self.boilerPlate(lock, locked)
364
365 def testWithSemaphore(self):
366 lock = threading.Semaphore()
367 def locked():
368 if lock.acquire(False):
369 lock.release()
370 return False
371 else:
372 return True
373 self.boilerPlate(lock, locked)
374
375 def testWithBoundedSemaphore(self):
376 lock = threading.BoundedSemaphore()
377 def locked():
378 if lock.acquire(False):
379 lock.release()
380 return False
381 else:
382 return True
383 self.boilerPlate(lock, locked)
384
Michael Foordb3a89842010-06-30 12:17:50 +0000385
386class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000387 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000388 started = False
389 exc = None
390 catch = False
391
392 def __enter__(self):
393 self.started = True
394 return self
395
396 def __exit__(self, *exc):
397 self.exc = exc
398 return self.catch
399
400
401class TestContextDecorator(unittest.TestCase):
402
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000403 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000404 def test_instance_docs(self):
405 # Issue 19330: ensure context manager instances have good docstrings
406 cm_docstring = mycontext.__doc__
407 obj = mycontext()
408 self.assertEqual(obj.__doc__, cm_docstring)
409
Michael Foordb3a89842010-06-30 12:17:50 +0000410 def test_contextdecorator(self):
411 context = mycontext()
412 with context as result:
413 self.assertIs(result, context)
414 self.assertTrue(context.started)
415
416 self.assertEqual(context.exc, (None, None, None))
417
418
419 def test_contextdecorator_with_exception(self):
420 context = mycontext()
421
Ezio Melottied3a7d22010-12-01 02:32:32 +0000422 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000423 with context:
424 raise NameError('foo')
425 self.assertIsNotNone(context.exc)
426 self.assertIs(context.exc[0], NameError)
427
428 context = mycontext()
429 context.catch = True
430 with context:
431 raise NameError('foo')
432 self.assertIsNotNone(context.exc)
433 self.assertIs(context.exc[0], NameError)
434
435
436 def test_decorator(self):
437 context = mycontext()
438
439 @context
440 def test():
441 self.assertIsNone(context.exc)
442 self.assertTrue(context.started)
443 test()
444 self.assertEqual(context.exc, (None, None, None))
445
446
447 def test_decorator_with_exception(self):
448 context = mycontext()
449
450 @context
451 def test():
452 self.assertIsNone(context.exc)
453 self.assertTrue(context.started)
454 raise NameError('foo')
455
Ezio Melottied3a7d22010-12-01 02:32:32 +0000456 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000457 test()
458 self.assertIsNotNone(context.exc)
459 self.assertIs(context.exc[0], NameError)
460
461
462 def test_decorating_method(self):
463 context = mycontext()
464
465 class Test(object):
466
467 @context
468 def method(self, a, b, c=None):
469 self.a = a
470 self.b = b
471 self.c = c
472
473 # these tests are for argument passing when used as a decorator
474 test = Test()
475 test.method(1, 2)
476 self.assertEqual(test.a, 1)
477 self.assertEqual(test.b, 2)
478 self.assertEqual(test.c, None)
479
480 test = Test()
481 test.method('a', 'b', 'c')
482 self.assertEqual(test.a, 'a')
483 self.assertEqual(test.b, 'b')
484 self.assertEqual(test.c, 'c')
485
486 test = Test()
487 test.method(a=1, b=2)
488 self.assertEqual(test.a, 1)
489 self.assertEqual(test.b, 2)
490
491
492 def test_typo_enter(self):
493 class mycontext(ContextDecorator):
494 def __unter__(self):
495 pass
496 def __exit__(self, *exc):
497 pass
498
499 with self.assertRaises(AttributeError):
500 with mycontext():
501 pass
502
503
504 def test_typo_exit(self):
505 class mycontext(ContextDecorator):
506 def __enter__(self):
507 pass
508 def __uxit__(self, *exc):
509 pass
510
511 with self.assertRaises(AttributeError):
512 with mycontext():
513 pass
514
515
516 def test_contextdecorator_as_mixin(self):
517 class somecontext(object):
518 started = False
519 exc = None
520
521 def __enter__(self):
522 self.started = True
523 return self
524
525 def __exit__(self, *exc):
526 self.exc = exc
527
528 class mycontext(somecontext, ContextDecorator):
529 pass
530
531 context = mycontext()
532 @context
533 def test():
534 self.assertIsNone(context.exc)
535 self.assertTrue(context.started)
536 test()
537 self.assertEqual(context.exc, (None, None, None))
538
539
540 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000541 @contextmanager
542 def woohoo(y):
543 state.append(y)
544 yield
545 state.append(999)
546
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000547 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000548 @woohoo(1)
549 def test(x):
550 self.assertEqual(state, [1])
551 state.append(x)
552 test('something')
553 self.assertEqual(state, [1, 'something', 999])
554
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000555 # Issue #11647: Ensure the decorated function is 'reusable'
556 state = []
557 test('something else')
558 self.assertEqual(state, [1, 'something else', 999])
559
Michael Foordb3a89842010-06-30 12:17:50 +0000560
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800561class TestBaseExitStack:
562 exit_stack = None
Nick Coghlan3267a302012-05-21 22:54:43 +1000563
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000564 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000565 def test_instance_docs(self):
566 # Issue 19330: ensure context manager instances have good docstrings
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800567 cm_docstring = self.exit_stack.__doc__
568 obj = self.exit_stack()
Nick Coghlan059def52013-10-26 18:08:15 +1000569 self.assertEqual(obj.__doc__, cm_docstring)
570
Nick Coghlan3267a302012-05-21 22:54:43 +1000571 def test_no_resources(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800572 with self.exit_stack():
Nick Coghlan3267a302012-05-21 22:54:43 +1000573 pass
574
575 def test_callback(self):
576 expected = [
577 ((), {}),
578 ((1,), {}),
579 ((1,2), {}),
580 ((), dict(example=1)),
581 ((1,), dict(example=1)),
582 ((1,2), dict(example=1)),
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300583 ((1,2), dict(self=3, callback=4)),
Nick Coghlan3267a302012-05-21 22:54:43 +1000584 ]
585 result = []
586 def _exit(*args, **kwds):
587 """Test metadata propagation"""
588 result.append((args, kwds))
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800589 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000590 for args, kwds in reversed(expected):
591 if args and kwds:
592 f = stack.callback(_exit, *args, **kwds)
593 elif args:
594 f = stack.callback(_exit, *args)
595 elif kwds:
596 f = stack.callback(_exit, **kwds)
597 else:
598 f = stack.callback(_exit)
599 self.assertIs(f, _exit)
600 for wrapper in stack._exit_callbacks:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800601 self.assertIs(wrapper[1].__wrapped__, _exit)
602 self.assertNotEqual(wrapper[1].__name__, _exit.__name__)
603 self.assertIsNone(wrapper[1].__doc__, _exit.__doc__)
Nick Coghlan3267a302012-05-21 22:54:43 +1000604 self.assertEqual(result, expected)
605
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300606 result = []
607 with self.exit_stack() as stack:
608 with self.assertRaises(TypeError):
609 stack.callback(arg=1)
610 with self.assertRaises(TypeError):
611 self.exit_stack.callback(arg=2)
Serhiy Storchaka142566c2019-06-05 18:22:31 +0300612 with self.assertRaises(TypeError):
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300613 stack.callback(callback=_exit, arg=3)
Serhiy Storchaka142566c2019-06-05 18:22:31 +0300614 self.assertEqual(result, [])
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300615
Nick Coghlan3267a302012-05-21 22:54:43 +1000616 def test_push(self):
617 exc_raised = ZeroDivisionError
618 def _expect_exc(exc_type, exc, exc_tb):
619 self.assertIs(exc_type, exc_raised)
620 def _suppress_exc(*exc_details):
621 return True
622 def _expect_ok(exc_type, exc, exc_tb):
623 self.assertIsNone(exc_type)
624 self.assertIsNone(exc)
625 self.assertIsNone(exc_tb)
626 class ExitCM(object):
627 def __init__(self, check_exc):
628 self.check_exc = check_exc
629 def __enter__(self):
630 self.fail("Should not be called!")
631 def __exit__(self, *exc_details):
632 self.check_exc(*exc_details)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800633 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000634 stack.push(_expect_ok)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800635 self.assertIs(stack._exit_callbacks[-1][1], _expect_ok)
Nick Coghlan3267a302012-05-21 22:54:43 +1000636 cm = ExitCM(_expect_ok)
637 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800638 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000639 stack.push(_suppress_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800640 self.assertIs(stack._exit_callbacks[-1][1], _suppress_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000641 cm = ExitCM(_expect_exc)
642 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800643 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000644 stack.push(_expect_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800645 self.assertIs(stack._exit_callbacks[-1][1], _expect_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000646 stack.push(_expect_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800647 self.assertIs(stack._exit_callbacks[-1][1], _expect_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000648 1/0
649
650 def test_enter_context(self):
651 class TestCM(object):
652 def __enter__(self):
653 result.append(1)
654 def __exit__(self, *exc_details):
655 result.append(3)
656
657 result = []
658 cm = TestCM()
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800659 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000660 @stack.callback # Registered first => cleaned up last
661 def _exit():
662 result.append(4)
663 self.assertIsNotNone(_exit)
664 stack.enter_context(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800665 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000666 result.append(2)
667 self.assertEqual(result, [1, 2, 3, 4])
668
669 def test_close(self):
670 result = []
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800671 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000672 @stack.callback
673 def _exit():
674 result.append(1)
675 self.assertIsNotNone(_exit)
676 stack.close()
677 result.append(2)
678 self.assertEqual(result, [1, 2])
679
680 def test_pop_all(self):
681 result = []
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800682 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000683 @stack.callback
684 def _exit():
685 result.append(3)
686 self.assertIsNotNone(_exit)
687 new_stack = stack.pop_all()
688 result.append(1)
689 result.append(2)
690 new_stack.close()
691 self.assertEqual(result, [1, 2, 3])
692
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000693 def test_exit_raise(self):
694 with self.assertRaises(ZeroDivisionError):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800695 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000696 stack.push(lambda *exc: False)
697 1/0
698
699 def test_exit_suppress(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800700 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000701 stack.push(lambda *exc: True)
702 1/0
703
704 def test_exit_exception_chaining_reference(self):
705 # Sanity check to make sure that ExitStack chaining matches
706 # actual nested with statements
707 class RaiseExc:
708 def __init__(self, exc):
709 self.exc = exc
710 def __enter__(self):
711 return self
712 def __exit__(self, *exc_details):
713 raise self.exc
714
Nick Coghlan77452fc2012-06-01 22:48:32 +1000715 class RaiseExcWithContext:
716 def __init__(self, outer, inner):
717 self.outer = outer
718 self.inner = inner
719 def __enter__(self):
720 return self
721 def __exit__(self, *exc_details):
722 try:
723 raise self.inner
724 except:
725 raise self.outer
726
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000727 class SuppressExc:
728 def __enter__(self):
729 return self
730 def __exit__(self, *exc_details):
731 type(self).saved_details = exc_details
732 return True
733
734 try:
735 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000736 with RaiseExcWithContext(KeyError, AttributeError):
737 with SuppressExc():
738 with RaiseExc(ValueError):
739 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000740 except IndexError as exc:
741 self.assertIsInstance(exc.__context__, KeyError)
742 self.assertIsInstance(exc.__context__.__context__, AttributeError)
743 # Inner exceptions were suppressed
744 self.assertIsNone(exc.__context__.__context__.__context__)
745 else:
746 self.fail("Expected IndexError, but no exception was raised")
747 # Check the inner exceptions
748 inner_exc = SuppressExc.saved_details[1]
749 self.assertIsInstance(inner_exc, ValueError)
750 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
751
752 def test_exit_exception_chaining(self):
753 # Ensure exception chaining matches the reference behaviour
754 def raise_exc(exc):
755 raise exc
756
757 saved_details = None
758 def suppress_exc(*exc_details):
759 nonlocal saved_details
760 saved_details = exc_details
761 return True
762
763 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800764 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000765 stack.callback(raise_exc, IndexError)
766 stack.callback(raise_exc, KeyError)
767 stack.callback(raise_exc, AttributeError)
768 stack.push(suppress_exc)
769 stack.callback(raise_exc, ValueError)
770 1 / 0
771 except IndexError as exc:
772 self.assertIsInstance(exc.__context__, KeyError)
773 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000774 # Inner exceptions were suppressed
775 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000776 else:
777 self.fail("Expected IndexError, but no exception was raised")
778 # Check the inner exceptions
779 inner_exc = saved_details[1]
780 self.assertIsInstance(inner_exc, ValueError)
781 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
782
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000783 def test_exit_exception_non_suppressing(self):
784 # http://bugs.python.org/issue19092
785 def raise_exc(exc):
786 raise exc
787
788 def suppress_exc(*exc_details):
789 return True
790
791 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800792 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000793 stack.callback(lambda: None)
794 stack.callback(raise_exc, IndexError)
795 except Exception as exc:
796 self.assertIsInstance(exc, IndexError)
797 else:
798 self.fail("Expected IndexError, but no exception was raised")
799
800 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800801 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000802 stack.callback(raise_exc, KeyError)
803 stack.push(suppress_exc)
804 stack.callback(raise_exc, IndexError)
805 except Exception as exc:
806 self.assertIsInstance(exc, KeyError)
807 else:
808 self.fail("Expected KeyError, but no exception was raised")
809
Nick Coghlan09761e72014-01-22 22:24:46 +1000810 def test_exit_exception_with_correct_context(self):
811 # http://bugs.python.org/issue20317
812 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000813 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000814 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000815 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000816 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000817 raise exc
818
819 exc1 = Exception(1)
820 exc2 = Exception(2)
821 exc3 = Exception(3)
822 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000823
824 # The contextmanager already fixes the context, so prior to the
825 # fix, ExitStack would try to fix it *again* and get into an
826 # infinite self-referential loop
827 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800828 with self.exit_stack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000829 stack.enter_context(gets_the_context_right(exc4))
830 stack.enter_context(gets_the_context_right(exc3))
831 stack.enter_context(gets_the_context_right(exc2))
832 raise exc1
833 except Exception as exc:
834 self.assertIs(exc, exc4)
835 self.assertIs(exc.__context__, exc3)
836 self.assertIs(exc.__context__.__context__, exc2)
837 self.assertIs(exc.__context__.__context__.__context__, exc1)
838 self.assertIsNone(
839 exc.__context__.__context__.__context__.__context__)
840
841 def test_exit_exception_with_existing_context(self):
842 # Addresses a lack of test coverage discovered after checking in a
843 # fix for issue 20317 that still contained debugging code.
844 def raise_nested(inner_exc, outer_exc):
845 try:
846 raise inner_exc
847 finally:
848 raise outer_exc
849 exc1 = Exception(1)
850 exc2 = Exception(2)
851 exc3 = Exception(3)
852 exc4 = Exception(4)
853 exc5 = Exception(5)
854 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800855 with self.exit_stack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000856 stack.callback(raise_nested, exc4, exc5)
857 stack.callback(raise_nested, exc2, exc3)
858 raise exc1
859 except Exception as exc:
860 self.assertIs(exc, exc5)
861 self.assertIs(exc.__context__, exc4)
862 self.assertIs(exc.__context__.__context__, exc3)
863 self.assertIs(exc.__context__.__context__.__context__, exc2)
864 self.assertIs(
865 exc.__context__.__context__.__context__.__context__, exc1)
866 self.assertIsNone(
867 exc.__context__.__context__.__context__.__context__.__context__)
868
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000869 def test_body_exception_suppress(self):
870 def suppress_exc(*exc_details):
871 return True
872 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800873 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000874 stack.push(suppress_exc)
875 1/0
876 except IndexError as exc:
877 self.fail("Expected no exception, got IndexError")
878
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000879 def test_exit_exception_chaining_suppress(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800880 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000881 stack.push(lambda *exc: True)
882 stack.push(lambda *exc: 1/0)
883 stack.push(lambda *exc: {}[1])
884
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000885 def test_excessive_nesting(self):
886 # The original implementation would die with RecursionError here
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800887 with self.exit_stack() as stack:
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000888 for i in range(10000):
889 stack.callback(int)
890
Nick Coghlan3267a302012-05-21 22:54:43 +1000891 def test_instance_bypass(self):
892 class Example(object): pass
893 cm = Example()
894 cm.__exit__ = object()
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800895 stack = self.exit_stack()
Nick Coghlan3267a302012-05-21 22:54:43 +1000896 self.assertRaises(AttributeError, stack.enter_context, cm)
897 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800898 self.assertIs(stack._exit_callbacks[-1][1], cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000899
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700900 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300901 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700902 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300903 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700904
905 @contextmanager
906 def second():
907 try:
908 yield 1
909 except Exception as exc:
910 raise UniqueException("new exception") from exc
911
912 @contextmanager
913 def first():
914 try:
915 yield 1
916 except Exception as exc:
917 raise exc
918
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300919 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700920 # handler which chain raised a new UniqueException.
921 with self.assertRaises(UniqueException) as err_ctx:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800922 with self.exit_stack() as es_ctx:
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700923 es_ctx.enter_context(second())
924 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300925 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700926
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300927 exc = err_ctx.exception
928 self.assertIsInstance(exc, UniqueException)
929 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
930 self.assertIsNone(exc.__context__.__context__)
931 self.assertIsNone(exc.__context__.__cause__)
932 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700933
Berker Peksagbb44fe02014-11-28 23:28:06 +0200934
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800935class TestExitStack(TestBaseExitStack, unittest.TestCase):
936 exit_stack = ExitStack
937
938
Berker Peksagbb44fe02014-11-28 23:28:06 +0200939class TestRedirectStream:
940
941 redirect_stream = None
942 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700943
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000944 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000945 def test_instance_docs(self):
946 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200947 cm_docstring = self.redirect_stream.__doc__
948 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000949 self.assertEqual(obj.__doc__, cm_docstring)
950
Nick Coghlan8e113b42013-11-03 17:00:51 +1000951 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200952 orig_stdout = getattr(sys, self.orig_stream)
953 self.redirect_stream(None)
954 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000955
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700956 def test_redirect_to_string_io(self):
957 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000958 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200959 orig_stdout = getattr(sys, self.orig_stream)
960 with self.redirect_stream(f):
961 print(msg, file=getattr(sys, self.orig_stream))
962 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000963 s = f.getvalue().strip()
964 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000965
Nick Coghlan8608d262013-10-20 00:30:51 +1000966 def test_enter_result_is_target(self):
967 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200968 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000969 self.assertIs(enter_result, f)
970
971 def test_cm_is_reusable(self):
972 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200973 write_to_f = self.redirect_stream(f)
974 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000975 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200976 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000977 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200978 print("World!", file=getattr(sys, self.orig_stream))
979 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000980 s = f.getvalue()
981 self.assertEqual(s, "Hello World!\n")
982
Nick Coghlan8e113b42013-11-03 17:00:51 +1000983 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000984 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200985 write_to_f = self.redirect_stream(f)
986 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000987 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200988 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000989 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200990 print("World!", file=getattr(sys, self.orig_stream))
991 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000992 s = f.getvalue()
993 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000994
995
Berker Peksagbb44fe02014-11-28 23:28:06 +0200996class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
997
998 redirect_stream = redirect_stdout
999 orig_stream = "stdout"
1000
1001
1002class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
1003
1004 redirect_stream = redirect_stderr
1005 orig_stream = "stderr"
1006
1007
Nick Coghlan240f86d2013-10-17 23:40:57 +10001008class TestSuppress(unittest.TestCase):
1009
Nick Coghlan561eb5c2013-10-26 22:20:43 +10001010 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +10001011 def test_instance_docs(self):
1012 # Issue 19330: ensure context manager instances have good docstrings
1013 cm_docstring = suppress.__doc__
1014 obj = suppress()
1015 self.assertEqual(obj.__doc__, cm_docstring)
1016
Nick Coghlan8608d262013-10-20 00:30:51 +10001017 def test_no_result_from_enter(self):
1018 with suppress(ValueError) as enter_result:
1019 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +10001020
Nick Coghlan8608d262013-10-20 00:30:51 +10001021 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +10001022 with suppress(ValueError):
1023 self.assertEqual(pow(2, 5), 32)
1024
1025 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +10001026 with suppress(TypeError):
1027 len(5)
1028
Nick Coghlan059def52013-10-26 18:08:15 +10001029 def test_exception_hierarchy(self):
1030 with suppress(LookupError):
1031 'Hello'[50]
1032
1033 def test_other_exception(self):
1034 with self.assertRaises(ZeroDivisionError):
1035 with suppress(TypeError):
1036 1/0
1037
1038 def test_no_args(self):
1039 with self.assertRaises(ZeroDivisionError):
1040 with suppress():
1041 1/0
1042
Nick Coghlan240f86d2013-10-17 23:40:57 +10001043 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +10001044 with suppress(ZeroDivisionError, TypeError):
1045 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +10001046 with suppress(ZeroDivisionError, TypeError):
1047 len(5)
1048
Nick Coghlan8608d262013-10-20 00:30:51 +10001049 def test_cm_is_reentrant(self):
1050 ignore_exceptions = suppress(Exception)
1051 with ignore_exceptions:
1052 pass
1053 with ignore_exceptions:
1054 len(5)
1055 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +10001056 with ignore_exceptions: # Check nested usage
1057 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +00001058 outer_continued = True
1059 1/0
1060 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +10001061
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001062if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -04001063 unittest.main()