blob: 64b6578ff94eb30954694dfda5ae02bda6f6d756 [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
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000010
Florent Xicluna41fe6152010-04-02 18:52:12 +000011
Brett Cannon9e080e02016-04-08 12:15:27 -070012class TestAbstractContextManager(unittest.TestCase):
13
14 def test_enter(self):
15 class DefaultEnter(AbstractContextManager):
16 def __exit__(self, *args):
17 super().__exit__(*args)
18
19 manager = DefaultEnter()
20 self.assertIs(manager.__enter__(), manager)
21
22 def test_exit_is_abstract(self):
23 class MissingExit(AbstractContextManager):
24 pass
25
26 with self.assertRaises(TypeError):
27 MissingExit()
28
29 def test_structural_subclassing(self):
30 class ManagerFromScratch:
31 def __enter__(self):
32 return self
33 def __exit__(self, exc_type, exc_value, traceback):
34 return None
35
36 self.assertTrue(issubclass(ManagerFromScratch, AbstractContextManager))
37
38 class DefaultEnter(AbstractContextManager):
39 def __exit__(self, *args):
40 super().__exit__(*args)
41
42 self.assertTrue(issubclass(DefaultEnter, AbstractContextManager))
43
Jelle Zijlstra57161aa2017-06-09 08:21:47 -070044 class NoEnter(ManagerFromScratch):
45 __enter__ = None
46
47 self.assertFalse(issubclass(NoEnter, AbstractContextManager))
48
49 class NoExit(ManagerFromScratch):
50 __exit__ = None
51
52 self.assertFalse(issubclass(NoExit, AbstractContextManager))
53
Brett Cannon9e080e02016-04-08 12:15:27 -070054
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000055class ContextManagerTestCase(unittest.TestCase):
56
57 def test_contextmanager_plain(self):
58 state = []
59 @contextmanager
60 def woohoo():
61 state.append(1)
62 yield 42
63 state.append(999)
64 with woohoo() as x:
65 self.assertEqual(state, [1])
66 self.assertEqual(x, 42)
67 state.append(x)
68 self.assertEqual(state, [1, 42, 999])
69
70 def test_contextmanager_finally(self):
71 state = []
72 @contextmanager
73 def woohoo():
74 state.append(1)
75 try:
76 yield 42
77 finally:
78 state.append(999)
Florent Xicluna41fe6152010-04-02 18:52:12 +000079 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000080 with woohoo() as x:
81 self.assertEqual(state, [1])
82 self.assertEqual(x, 42)
83 state.append(x)
84 raise ZeroDivisionError()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000085 self.assertEqual(state, [1, 42, 999])
86
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000087 def test_contextmanager_no_reraise(self):
88 @contextmanager
89 def whee():
90 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000091 ctx = whee()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000092 ctx.__enter__()
93 # Calling __exit__ should not result in an exception
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000094 self.assertFalse(ctx.__exit__(TypeError, TypeError("foo"), None))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000095
96 def test_contextmanager_trap_yield_after_throw(self):
97 @contextmanager
98 def whoo():
99 try:
100 yield
101 except:
102 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +0000103 ctx = whoo()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000104 ctx.__enter__()
105 self.assertRaises(
106 RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
107 )
108
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000109 def test_contextmanager_except(self):
110 state = []
111 @contextmanager
112 def woohoo():
113 state.append(1)
114 try:
115 yield 42
Guido van Rossumb940e112007-01-10 16:19:56 +0000116 except ZeroDivisionError as e:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000117 state.append(e.args[0])
118 self.assertEqual(state, [1, 42, 999])
119 with woohoo() as x:
120 self.assertEqual(state, [1])
121 self.assertEqual(x, 42)
122 state.append(x)
123 raise ZeroDivisionError(999)
124 self.assertEqual(state, [1, 42, 999])
125
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400126 def test_contextmanager_except_stopiter(self):
127 stop_exc = StopIteration('spam')
128 @contextmanager
129 def woohoo():
130 yield
131 try:
Martin Panter7e3a91a2016-02-10 04:40:48 +0000132 with self.assertWarnsRegex(DeprecationWarning,
Yury Selivanov68333392015-05-22 11:16:47 -0400133 "StopIteration"):
134 with woohoo():
135 raise stop_exc
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400136 except Exception as ex:
137 self.assertIs(ex, stop_exc)
138 else:
139 self.fail('StopIteration was suppressed')
140
141 def test_contextmanager_except_pep479(self):
142 code = """\
143from __future__ import generator_stop
144from contextlib import contextmanager
145@contextmanager
146def woohoo():
147 yield
148"""
149 locals = {}
150 exec(code, locals, locals)
151 woohoo = locals['woohoo']
152
153 stop_exc = StopIteration('spam')
154 try:
155 with woohoo():
156 raise stop_exc
157 except Exception as ex:
158 self.assertIs(ex, stop_exc)
159 else:
160 self.fail('StopIteration was suppressed')
161
svelankar00c75e92017-04-11 05:11:13 -0400162 def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
163 @contextmanager
164 def test_issue29692():
165 try:
166 yield
167 except Exception as exc:
168 raise RuntimeError('issue29692:Chained') from exc
169 try:
170 with test_issue29692():
171 raise ZeroDivisionError
172 except Exception as ex:
173 self.assertIs(type(ex), RuntimeError)
174 self.assertEqual(ex.args[0], 'issue29692:Chained')
175 self.assertIsInstance(ex.__cause__, ZeroDivisionError)
176
177 try:
178 with test_issue29692():
179 raise StopIteration('issue29692:Unchained')
180 except Exception as ex:
181 self.assertIs(type(ex), StopIteration)
182 self.assertEqual(ex.args[0], 'issue29692:Unchained')
183 self.assertIsNone(ex.__cause__)
184
R. David Murray378c0cf2010-02-24 01:46:21 +0000185 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000186 def attribs(**kw):
187 def decorate(func):
188 for k,v in kw.items():
189 setattr(func,k,v)
190 return func
191 return decorate
192 @contextmanager
193 @attribs(foo='bar')
194 def baz(spam):
195 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000196 return baz
197
198 def test_contextmanager_attribs(self):
199 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000200 self.assertEqual(baz.__name__,'baz')
201 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000202
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000203 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000204 def test_contextmanager_doc_attrib(self):
205 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000206 self.assertEqual(baz.__doc__, "Whee!")
207
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000208 @support.requires_docstrings
209 def test_instance_docstring_given_cm_docstring(self):
210 baz = self._create_contextmanager_attribs()(None)
211 self.assertEqual(baz.__doc__, "Whee!")
212
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300213 def test_keywords(self):
214 # Ensure no keyword arguments are inhibited
215 @contextmanager
216 def woohoo(self, func, args, kwds):
217 yield (self, func, args, kwds)
218 with woohoo(self=11, func=22, args=33, kwds=44) as target:
219 self.assertEqual(target, (11, 22, 33, 44))
220
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000221
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000222class ClosingTestCase(unittest.TestCase):
223
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000224 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000225 def test_instance_docs(self):
226 # Issue 19330: ensure context manager instances have good docstrings
227 cm_docstring = closing.__doc__
228 obj = closing(None)
229 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000230
231 def test_closing(self):
232 state = []
233 class C:
234 def close(self):
235 state.append(1)
236 x = C()
237 self.assertEqual(state, [])
238 with closing(x) as y:
239 self.assertEqual(x, y)
240 self.assertEqual(state, [1])
241
242 def test_closing_error(self):
243 state = []
244 class C:
245 def close(self):
246 state.append(1)
247 x = C()
248 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000249 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000250 with closing(x) as y:
251 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000252 1 / 0
253 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000254
255class FileContextTestCase(unittest.TestCase):
256
257 def testWithOpen(self):
258 tfn = tempfile.mktemp()
259 try:
260 f = None
261 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000262 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000263 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000264 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000265 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000266 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000267 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000268 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000269 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000270 1 / 0
271 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000272 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000273 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000274
275class LockContextTestCase(unittest.TestCase):
276
277 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000278 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000279 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000280 self.assertTrue(locked())
281 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000282 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000283 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000284 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000285 1 / 0
286 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000287
288 def testWithLock(self):
289 lock = threading.Lock()
290 self.boilerPlate(lock, lock.locked)
291
292 def testWithRLock(self):
293 lock = threading.RLock()
294 self.boilerPlate(lock, lock._is_owned)
295
296 def testWithCondition(self):
297 lock = threading.Condition()
298 def locked():
299 return lock._is_owned()
300 self.boilerPlate(lock, locked)
301
302 def testWithSemaphore(self):
303 lock = threading.Semaphore()
304 def locked():
305 if lock.acquire(False):
306 lock.release()
307 return False
308 else:
309 return True
310 self.boilerPlate(lock, locked)
311
312 def testWithBoundedSemaphore(self):
313 lock = threading.BoundedSemaphore()
314 def locked():
315 if lock.acquire(False):
316 lock.release()
317 return False
318 else:
319 return True
320 self.boilerPlate(lock, locked)
321
Michael Foordb3a89842010-06-30 12:17:50 +0000322
323class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000324 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000325 started = False
326 exc = None
327 catch = False
328
329 def __enter__(self):
330 self.started = True
331 return self
332
333 def __exit__(self, *exc):
334 self.exc = exc
335 return self.catch
336
337
338class TestContextDecorator(unittest.TestCase):
339
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000340 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000341 def test_instance_docs(self):
342 # Issue 19330: ensure context manager instances have good docstrings
343 cm_docstring = mycontext.__doc__
344 obj = mycontext()
345 self.assertEqual(obj.__doc__, cm_docstring)
346
Michael Foordb3a89842010-06-30 12:17:50 +0000347 def test_contextdecorator(self):
348 context = mycontext()
349 with context as result:
350 self.assertIs(result, context)
351 self.assertTrue(context.started)
352
353 self.assertEqual(context.exc, (None, None, None))
354
355
356 def test_contextdecorator_with_exception(self):
357 context = mycontext()
358
Ezio Melottied3a7d22010-12-01 02:32:32 +0000359 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000360 with context:
361 raise NameError('foo')
362 self.assertIsNotNone(context.exc)
363 self.assertIs(context.exc[0], NameError)
364
365 context = mycontext()
366 context.catch = True
367 with context:
368 raise NameError('foo')
369 self.assertIsNotNone(context.exc)
370 self.assertIs(context.exc[0], NameError)
371
372
373 def test_decorator(self):
374 context = mycontext()
375
376 @context
377 def test():
378 self.assertIsNone(context.exc)
379 self.assertTrue(context.started)
380 test()
381 self.assertEqual(context.exc, (None, None, None))
382
383
384 def test_decorator_with_exception(self):
385 context = mycontext()
386
387 @context
388 def test():
389 self.assertIsNone(context.exc)
390 self.assertTrue(context.started)
391 raise NameError('foo')
392
Ezio Melottied3a7d22010-12-01 02:32:32 +0000393 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000394 test()
395 self.assertIsNotNone(context.exc)
396 self.assertIs(context.exc[0], NameError)
397
398
399 def test_decorating_method(self):
400 context = mycontext()
401
402 class Test(object):
403
404 @context
405 def method(self, a, b, c=None):
406 self.a = a
407 self.b = b
408 self.c = c
409
410 # these tests are for argument passing when used as a decorator
411 test = Test()
412 test.method(1, 2)
413 self.assertEqual(test.a, 1)
414 self.assertEqual(test.b, 2)
415 self.assertEqual(test.c, None)
416
417 test = Test()
418 test.method('a', 'b', 'c')
419 self.assertEqual(test.a, 'a')
420 self.assertEqual(test.b, 'b')
421 self.assertEqual(test.c, 'c')
422
423 test = Test()
424 test.method(a=1, b=2)
425 self.assertEqual(test.a, 1)
426 self.assertEqual(test.b, 2)
427
428
429 def test_typo_enter(self):
430 class mycontext(ContextDecorator):
431 def __unter__(self):
432 pass
433 def __exit__(self, *exc):
434 pass
435
436 with self.assertRaises(AttributeError):
437 with mycontext():
438 pass
439
440
441 def test_typo_exit(self):
442 class mycontext(ContextDecorator):
443 def __enter__(self):
444 pass
445 def __uxit__(self, *exc):
446 pass
447
448 with self.assertRaises(AttributeError):
449 with mycontext():
450 pass
451
452
453 def test_contextdecorator_as_mixin(self):
454 class somecontext(object):
455 started = False
456 exc = None
457
458 def __enter__(self):
459 self.started = True
460 return self
461
462 def __exit__(self, *exc):
463 self.exc = exc
464
465 class mycontext(somecontext, ContextDecorator):
466 pass
467
468 context = mycontext()
469 @context
470 def test():
471 self.assertIsNone(context.exc)
472 self.assertTrue(context.started)
473 test()
474 self.assertEqual(context.exc, (None, None, None))
475
476
477 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000478 @contextmanager
479 def woohoo(y):
480 state.append(y)
481 yield
482 state.append(999)
483
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000484 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000485 @woohoo(1)
486 def test(x):
487 self.assertEqual(state, [1])
488 state.append(x)
489 test('something')
490 self.assertEqual(state, [1, 'something', 999])
491
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000492 # Issue #11647: Ensure the decorated function is 'reusable'
493 state = []
494 test('something else')
495 self.assertEqual(state, [1, 'something else', 999])
496
Michael Foordb3a89842010-06-30 12:17:50 +0000497
Nick Coghlan3267a302012-05-21 22:54:43 +1000498class TestExitStack(unittest.TestCase):
499
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000500 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000501 def test_instance_docs(self):
502 # Issue 19330: ensure context manager instances have good docstrings
503 cm_docstring = ExitStack.__doc__
504 obj = ExitStack()
505 self.assertEqual(obj.__doc__, cm_docstring)
506
Nick Coghlan3267a302012-05-21 22:54:43 +1000507 def test_no_resources(self):
508 with ExitStack():
509 pass
510
511 def test_callback(self):
512 expected = [
513 ((), {}),
514 ((1,), {}),
515 ((1,2), {}),
516 ((), dict(example=1)),
517 ((1,), dict(example=1)),
518 ((1,2), dict(example=1)),
519 ]
520 result = []
521 def _exit(*args, **kwds):
522 """Test metadata propagation"""
523 result.append((args, kwds))
524 with ExitStack() as stack:
525 for args, kwds in reversed(expected):
526 if args and kwds:
527 f = stack.callback(_exit, *args, **kwds)
528 elif args:
529 f = stack.callback(_exit, *args)
530 elif kwds:
531 f = stack.callback(_exit, **kwds)
532 else:
533 f = stack.callback(_exit)
534 self.assertIs(f, _exit)
535 for wrapper in stack._exit_callbacks:
536 self.assertIs(wrapper.__wrapped__, _exit)
537 self.assertNotEqual(wrapper.__name__, _exit.__name__)
538 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
539 self.assertEqual(result, expected)
540
541 def test_push(self):
542 exc_raised = ZeroDivisionError
543 def _expect_exc(exc_type, exc, exc_tb):
544 self.assertIs(exc_type, exc_raised)
545 def _suppress_exc(*exc_details):
546 return True
547 def _expect_ok(exc_type, exc, exc_tb):
548 self.assertIsNone(exc_type)
549 self.assertIsNone(exc)
550 self.assertIsNone(exc_tb)
551 class ExitCM(object):
552 def __init__(self, check_exc):
553 self.check_exc = check_exc
554 def __enter__(self):
555 self.fail("Should not be called!")
556 def __exit__(self, *exc_details):
557 self.check_exc(*exc_details)
558 with ExitStack() as stack:
559 stack.push(_expect_ok)
560 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
561 cm = ExitCM(_expect_ok)
562 stack.push(cm)
563 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
564 stack.push(_suppress_exc)
565 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
566 cm = ExitCM(_expect_exc)
567 stack.push(cm)
568 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
569 stack.push(_expect_exc)
570 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
571 stack.push(_expect_exc)
572 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
573 1/0
574
575 def test_enter_context(self):
576 class TestCM(object):
577 def __enter__(self):
578 result.append(1)
579 def __exit__(self, *exc_details):
580 result.append(3)
581
582 result = []
583 cm = TestCM()
584 with ExitStack() as stack:
585 @stack.callback # Registered first => cleaned up last
586 def _exit():
587 result.append(4)
588 self.assertIsNotNone(_exit)
589 stack.enter_context(cm)
590 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
591 result.append(2)
592 self.assertEqual(result, [1, 2, 3, 4])
593
594 def test_close(self):
595 result = []
596 with ExitStack() as stack:
597 @stack.callback
598 def _exit():
599 result.append(1)
600 self.assertIsNotNone(_exit)
601 stack.close()
602 result.append(2)
603 self.assertEqual(result, [1, 2])
604
605 def test_pop_all(self):
606 result = []
607 with ExitStack() as stack:
608 @stack.callback
609 def _exit():
610 result.append(3)
611 self.assertIsNotNone(_exit)
612 new_stack = stack.pop_all()
613 result.append(1)
614 result.append(2)
615 new_stack.close()
616 self.assertEqual(result, [1, 2, 3])
617
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000618 def test_exit_raise(self):
619 with self.assertRaises(ZeroDivisionError):
620 with ExitStack() as stack:
621 stack.push(lambda *exc: False)
622 1/0
623
624 def test_exit_suppress(self):
625 with ExitStack() as stack:
626 stack.push(lambda *exc: True)
627 1/0
628
629 def test_exit_exception_chaining_reference(self):
630 # Sanity check to make sure that ExitStack chaining matches
631 # actual nested with statements
632 class RaiseExc:
633 def __init__(self, exc):
634 self.exc = exc
635 def __enter__(self):
636 return self
637 def __exit__(self, *exc_details):
638 raise self.exc
639
Nick Coghlan77452fc2012-06-01 22:48:32 +1000640 class RaiseExcWithContext:
641 def __init__(self, outer, inner):
642 self.outer = outer
643 self.inner = inner
644 def __enter__(self):
645 return self
646 def __exit__(self, *exc_details):
647 try:
648 raise self.inner
649 except:
650 raise self.outer
651
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000652 class SuppressExc:
653 def __enter__(self):
654 return self
655 def __exit__(self, *exc_details):
656 type(self).saved_details = exc_details
657 return True
658
659 try:
660 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000661 with RaiseExcWithContext(KeyError, AttributeError):
662 with SuppressExc():
663 with RaiseExc(ValueError):
664 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000665 except IndexError as exc:
666 self.assertIsInstance(exc.__context__, KeyError)
667 self.assertIsInstance(exc.__context__.__context__, AttributeError)
668 # Inner exceptions were suppressed
669 self.assertIsNone(exc.__context__.__context__.__context__)
670 else:
671 self.fail("Expected IndexError, but no exception was raised")
672 # Check the inner exceptions
673 inner_exc = SuppressExc.saved_details[1]
674 self.assertIsInstance(inner_exc, ValueError)
675 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
676
677 def test_exit_exception_chaining(self):
678 # Ensure exception chaining matches the reference behaviour
679 def raise_exc(exc):
680 raise exc
681
682 saved_details = None
683 def suppress_exc(*exc_details):
684 nonlocal saved_details
685 saved_details = exc_details
686 return True
687
688 try:
689 with ExitStack() as stack:
690 stack.callback(raise_exc, IndexError)
691 stack.callback(raise_exc, KeyError)
692 stack.callback(raise_exc, AttributeError)
693 stack.push(suppress_exc)
694 stack.callback(raise_exc, ValueError)
695 1 / 0
696 except IndexError as exc:
697 self.assertIsInstance(exc.__context__, KeyError)
698 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000699 # Inner exceptions were suppressed
700 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000701 else:
702 self.fail("Expected IndexError, but no exception was raised")
703 # Check the inner exceptions
704 inner_exc = saved_details[1]
705 self.assertIsInstance(inner_exc, ValueError)
706 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
707
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000708 def test_exit_exception_non_suppressing(self):
709 # http://bugs.python.org/issue19092
710 def raise_exc(exc):
711 raise exc
712
713 def suppress_exc(*exc_details):
714 return True
715
716 try:
717 with ExitStack() as stack:
718 stack.callback(lambda: None)
719 stack.callback(raise_exc, IndexError)
720 except Exception as exc:
721 self.assertIsInstance(exc, IndexError)
722 else:
723 self.fail("Expected IndexError, but no exception was raised")
724
725 try:
726 with ExitStack() as stack:
727 stack.callback(raise_exc, KeyError)
728 stack.push(suppress_exc)
729 stack.callback(raise_exc, IndexError)
730 except Exception as exc:
731 self.assertIsInstance(exc, KeyError)
732 else:
733 self.fail("Expected KeyError, but no exception was raised")
734
Nick Coghlan09761e72014-01-22 22:24:46 +1000735 def test_exit_exception_with_correct_context(self):
736 # http://bugs.python.org/issue20317
737 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000738 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000739 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000740 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000741 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000742 raise exc
743
744 exc1 = Exception(1)
745 exc2 = Exception(2)
746 exc3 = Exception(3)
747 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000748
749 # The contextmanager already fixes the context, so prior to the
750 # fix, ExitStack would try to fix it *again* and get into an
751 # infinite self-referential loop
752 try:
753 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000754 stack.enter_context(gets_the_context_right(exc4))
755 stack.enter_context(gets_the_context_right(exc3))
756 stack.enter_context(gets_the_context_right(exc2))
757 raise exc1
758 except Exception as exc:
759 self.assertIs(exc, exc4)
760 self.assertIs(exc.__context__, exc3)
761 self.assertIs(exc.__context__.__context__, exc2)
762 self.assertIs(exc.__context__.__context__.__context__, exc1)
763 self.assertIsNone(
764 exc.__context__.__context__.__context__.__context__)
765
766 def test_exit_exception_with_existing_context(self):
767 # Addresses a lack of test coverage discovered after checking in a
768 # fix for issue 20317 that still contained debugging code.
769 def raise_nested(inner_exc, outer_exc):
770 try:
771 raise inner_exc
772 finally:
773 raise outer_exc
774 exc1 = Exception(1)
775 exc2 = Exception(2)
776 exc3 = Exception(3)
777 exc4 = Exception(4)
778 exc5 = Exception(5)
779 try:
780 with ExitStack() as stack:
781 stack.callback(raise_nested, exc4, exc5)
782 stack.callback(raise_nested, exc2, exc3)
783 raise exc1
784 except Exception as exc:
785 self.assertIs(exc, exc5)
786 self.assertIs(exc.__context__, exc4)
787 self.assertIs(exc.__context__.__context__, exc3)
788 self.assertIs(exc.__context__.__context__.__context__, exc2)
789 self.assertIs(
790 exc.__context__.__context__.__context__.__context__, exc1)
791 self.assertIsNone(
792 exc.__context__.__context__.__context__.__context__.__context__)
793
Nick Coghlan09761e72014-01-22 22:24:46 +1000794
795
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000796 def test_body_exception_suppress(self):
797 def suppress_exc(*exc_details):
798 return True
799 try:
800 with ExitStack() as stack:
801 stack.push(suppress_exc)
802 1/0
803 except IndexError as exc:
804 self.fail("Expected no exception, got IndexError")
805
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000806 def test_exit_exception_chaining_suppress(self):
807 with ExitStack() as stack:
808 stack.push(lambda *exc: True)
809 stack.push(lambda *exc: 1/0)
810 stack.push(lambda *exc: {}[1])
811
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000812 def test_excessive_nesting(self):
813 # The original implementation would die with RecursionError here
814 with ExitStack() as stack:
815 for i in range(10000):
816 stack.callback(int)
817
Nick Coghlan3267a302012-05-21 22:54:43 +1000818 def test_instance_bypass(self):
819 class Example(object): pass
820 cm = Example()
821 cm.__exit__ = object()
822 stack = ExitStack()
823 self.assertRaises(AttributeError, stack.enter_context, cm)
824 stack.push(cm)
825 self.assertIs(stack._exit_callbacks[-1], cm)
826
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700827 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300828 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700829 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300830 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700831
832 @contextmanager
833 def second():
834 try:
835 yield 1
836 except Exception as exc:
837 raise UniqueException("new exception") from exc
838
839 @contextmanager
840 def first():
841 try:
842 yield 1
843 except Exception as exc:
844 raise exc
845
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300846 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700847 # handler which chain raised a new UniqueException.
848 with self.assertRaises(UniqueException) as err_ctx:
849 with ExitStack() as es_ctx:
850 es_ctx.enter_context(second())
851 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300852 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700853
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300854 exc = err_ctx.exception
855 self.assertIsInstance(exc, UniqueException)
856 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
857 self.assertIsNone(exc.__context__.__context__)
858 self.assertIsNone(exc.__context__.__cause__)
859 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700860
Berker Peksagbb44fe02014-11-28 23:28:06 +0200861
862class TestRedirectStream:
863
864 redirect_stream = None
865 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700866
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000867 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000868 def test_instance_docs(self):
869 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200870 cm_docstring = self.redirect_stream.__doc__
871 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000872 self.assertEqual(obj.__doc__, cm_docstring)
873
Nick Coghlan8e113b42013-11-03 17:00:51 +1000874 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200875 orig_stdout = getattr(sys, self.orig_stream)
876 self.redirect_stream(None)
877 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000878
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700879 def test_redirect_to_string_io(self):
880 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000881 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200882 orig_stdout = getattr(sys, self.orig_stream)
883 with self.redirect_stream(f):
884 print(msg, file=getattr(sys, self.orig_stream))
885 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000886 s = f.getvalue().strip()
887 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000888
Nick Coghlan8608d262013-10-20 00:30:51 +1000889 def test_enter_result_is_target(self):
890 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200891 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000892 self.assertIs(enter_result, f)
893
894 def test_cm_is_reusable(self):
895 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200896 write_to_f = self.redirect_stream(f)
897 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000898 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200899 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000900 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200901 print("World!", file=getattr(sys, self.orig_stream))
902 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000903 s = f.getvalue()
904 self.assertEqual(s, "Hello World!\n")
905
Nick Coghlan8e113b42013-11-03 17:00:51 +1000906 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000907 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200908 write_to_f = self.redirect_stream(f)
909 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000910 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200911 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000912 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200913 print("World!", file=getattr(sys, self.orig_stream))
914 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000915 s = f.getvalue()
916 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000917
918
Berker Peksagbb44fe02014-11-28 23:28:06 +0200919class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
920
921 redirect_stream = redirect_stdout
922 orig_stream = "stdout"
923
924
925class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
926
927 redirect_stream = redirect_stderr
928 orig_stream = "stderr"
929
930
Nick Coghlan240f86d2013-10-17 23:40:57 +1000931class TestSuppress(unittest.TestCase):
932
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000933 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000934 def test_instance_docs(self):
935 # Issue 19330: ensure context manager instances have good docstrings
936 cm_docstring = suppress.__doc__
937 obj = suppress()
938 self.assertEqual(obj.__doc__, cm_docstring)
939
Nick Coghlan8608d262013-10-20 00:30:51 +1000940 def test_no_result_from_enter(self):
941 with suppress(ValueError) as enter_result:
942 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000943
Nick Coghlan8608d262013-10-20 00:30:51 +1000944 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000945 with suppress(ValueError):
946 self.assertEqual(pow(2, 5), 32)
947
948 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000949 with suppress(TypeError):
950 len(5)
951
Nick Coghlan059def52013-10-26 18:08:15 +1000952 def test_exception_hierarchy(self):
953 with suppress(LookupError):
954 'Hello'[50]
955
956 def test_other_exception(self):
957 with self.assertRaises(ZeroDivisionError):
958 with suppress(TypeError):
959 1/0
960
961 def test_no_args(self):
962 with self.assertRaises(ZeroDivisionError):
963 with suppress():
964 1/0
965
Nick Coghlan240f86d2013-10-17 23:40:57 +1000966 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000967 with suppress(ZeroDivisionError, TypeError):
968 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000969 with suppress(ZeroDivisionError, TypeError):
970 len(5)
971
Nick Coghlan8608d262013-10-20 00:30:51 +1000972 def test_cm_is_reentrant(self):
973 ignore_exceptions = suppress(Exception)
974 with ignore_exceptions:
975 pass
976 with ignore_exceptions:
977 len(5)
978 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +1000979 with ignore_exceptions: # Check nested usage
980 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +0000981 outer_continued = True
982 1/0
983 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +1000984
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000985if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400986 unittest.main()