blob: b1a467d952da0fbb511c2d596dcd7d946f5a1e20 [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
6import unittest
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00007from contextlib import * # Tests __all__
Benjamin Petersonee8712c2008-05-20 21:35:26 +00008from test import support
Victor Stinner45df8202010-04-28 22:31:17 +00009try:
10 import threading
11except ImportError:
12 threading = None
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000013
Florent Xicluna41fe6152010-04-02 18:52:12 +000014
Brett Cannon9e080e02016-04-08 12:15:27 -070015class TestAbstractContextManager(unittest.TestCase):
16
17 def test_enter(self):
18 class DefaultEnter(AbstractContextManager):
19 def __exit__(self, *args):
20 super().__exit__(*args)
21
22 manager = DefaultEnter()
23 self.assertIs(manager.__enter__(), manager)
24
25 def test_exit_is_abstract(self):
26 class MissingExit(AbstractContextManager):
27 pass
28
29 with self.assertRaises(TypeError):
30 MissingExit()
31
32 def test_structural_subclassing(self):
33 class ManagerFromScratch:
34 def __enter__(self):
35 return self
36 def __exit__(self, exc_type, exc_value, traceback):
37 return None
38
39 self.assertTrue(issubclass(ManagerFromScratch, AbstractContextManager))
40
41 class DefaultEnter(AbstractContextManager):
42 def __exit__(self, *args):
43 super().__exit__(*args)
44
45 self.assertTrue(issubclass(DefaultEnter, AbstractContextManager))
46
47
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000048class ContextManagerTestCase(unittest.TestCase):
49
50 def test_contextmanager_plain(self):
51 state = []
52 @contextmanager
53 def woohoo():
54 state.append(1)
55 yield 42
56 state.append(999)
57 with woohoo() as x:
58 self.assertEqual(state, [1])
59 self.assertEqual(x, 42)
60 state.append(x)
61 self.assertEqual(state, [1, 42, 999])
62
63 def test_contextmanager_finally(self):
64 state = []
65 @contextmanager
66 def woohoo():
67 state.append(1)
68 try:
69 yield 42
70 finally:
71 state.append(999)
Florent Xicluna41fe6152010-04-02 18:52:12 +000072 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000073 with woohoo() as x:
74 self.assertEqual(state, [1])
75 self.assertEqual(x, 42)
76 state.append(x)
77 raise ZeroDivisionError()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000078 self.assertEqual(state, [1, 42, 999])
79
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000080 def test_contextmanager_no_reraise(self):
81 @contextmanager
82 def whee():
83 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000084 ctx = whee()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000085 ctx.__enter__()
86 # Calling __exit__ should not result in an exception
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000087 self.assertFalse(ctx.__exit__(TypeError, TypeError("foo"), None))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000088
89 def test_contextmanager_trap_yield_after_throw(self):
90 @contextmanager
91 def whoo():
92 try:
93 yield
94 except:
95 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000096 ctx = whoo()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000097 ctx.__enter__()
98 self.assertRaises(
99 RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
100 )
101
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000102 def test_contextmanager_except(self):
103 state = []
104 @contextmanager
105 def woohoo():
106 state.append(1)
107 try:
108 yield 42
Guido van Rossumb940e112007-01-10 16:19:56 +0000109 except ZeroDivisionError as e:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000110 state.append(e.args[0])
111 self.assertEqual(state, [1, 42, 999])
112 with woohoo() as x:
113 self.assertEqual(state, [1])
114 self.assertEqual(x, 42)
115 state.append(x)
116 raise ZeroDivisionError(999)
117 self.assertEqual(state, [1, 42, 999])
118
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400119 def test_contextmanager_except_stopiter(self):
120 stop_exc = StopIteration('spam')
121 @contextmanager
122 def woohoo():
123 yield
124 try:
Martin Panter7e3a91a2016-02-10 04:40:48 +0000125 with self.assertWarnsRegex(DeprecationWarning,
Yury Selivanov68333392015-05-22 11:16:47 -0400126 "StopIteration"):
127 with woohoo():
128 raise stop_exc
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400129 except Exception as ex:
130 self.assertIs(ex, stop_exc)
131 else:
132 self.fail('StopIteration was suppressed')
133
134 def test_contextmanager_except_pep479(self):
135 code = """\
136from __future__ import generator_stop
137from contextlib import contextmanager
138@contextmanager
139def woohoo():
140 yield
141"""
142 locals = {}
143 exec(code, locals, locals)
144 woohoo = locals['woohoo']
145
146 stop_exc = StopIteration('spam')
147 try:
148 with woohoo():
149 raise stop_exc
150 except Exception as ex:
151 self.assertIs(ex, stop_exc)
152 else:
153 self.fail('StopIteration was suppressed')
154
svelankar00c75e92017-04-11 05:11:13 -0400155 def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
156 @contextmanager
157 def test_issue29692():
158 try:
159 yield
160 except Exception as exc:
161 raise RuntimeError('issue29692:Chained') from exc
162 try:
163 with test_issue29692():
164 raise ZeroDivisionError
165 except Exception as ex:
166 self.assertIs(type(ex), RuntimeError)
167 self.assertEqual(ex.args[0], 'issue29692:Chained')
168 self.assertIsInstance(ex.__cause__, ZeroDivisionError)
169
170 try:
171 with test_issue29692():
172 raise StopIteration('issue29692:Unchained')
173 except Exception as ex:
174 self.assertIs(type(ex), StopIteration)
175 self.assertEqual(ex.args[0], 'issue29692:Unchained')
176 self.assertIsNone(ex.__cause__)
177
R. David Murray378c0cf2010-02-24 01:46:21 +0000178 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000179 def attribs(**kw):
180 def decorate(func):
181 for k,v in kw.items():
182 setattr(func,k,v)
183 return func
184 return decorate
185 @contextmanager
186 @attribs(foo='bar')
187 def baz(spam):
188 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000189 return baz
190
191 def test_contextmanager_attribs(self):
192 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000193 self.assertEqual(baz.__name__,'baz')
194 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000195
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000196 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000197 def test_contextmanager_doc_attrib(self):
198 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000199 self.assertEqual(baz.__doc__, "Whee!")
200
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000201 @support.requires_docstrings
202 def test_instance_docstring_given_cm_docstring(self):
203 baz = self._create_contextmanager_attribs()(None)
204 self.assertEqual(baz.__doc__, "Whee!")
205
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300206 def test_keywords(self):
207 # Ensure no keyword arguments are inhibited
208 @contextmanager
209 def woohoo(self, func, args, kwds):
210 yield (self, func, args, kwds)
211 with woohoo(self=11, func=22, args=33, kwds=44) as target:
212 self.assertEqual(target, (11, 22, 33, 44))
213
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000214
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000215class ClosingTestCase(unittest.TestCase):
216
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000217 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000218 def test_instance_docs(self):
219 # Issue 19330: ensure context manager instances have good docstrings
220 cm_docstring = closing.__doc__
221 obj = closing(None)
222 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000223
224 def test_closing(self):
225 state = []
226 class C:
227 def close(self):
228 state.append(1)
229 x = C()
230 self.assertEqual(state, [])
231 with closing(x) as y:
232 self.assertEqual(x, y)
233 self.assertEqual(state, [1])
234
235 def test_closing_error(self):
236 state = []
237 class C:
238 def close(self):
239 state.append(1)
240 x = C()
241 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000242 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000243 with closing(x) as y:
244 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000245 1 / 0
246 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000247
248class FileContextTestCase(unittest.TestCase):
249
250 def testWithOpen(self):
251 tfn = tempfile.mktemp()
252 try:
253 f = None
254 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000255 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000256 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000257 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000258 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000259 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000260 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000261 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000262 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000263 1 / 0
264 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000265 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000266 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000267
Victor Stinner45df8202010-04-28 22:31:17 +0000268@unittest.skipUnless(threading, 'Threading required for this test.')
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000269class LockContextTestCase(unittest.TestCase):
270
271 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000272 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000273 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000274 self.assertTrue(locked())
275 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000276 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000277 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000278 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000279 1 / 0
280 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000281
282 def testWithLock(self):
283 lock = threading.Lock()
284 self.boilerPlate(lock, lock.locked)
285
286 def testWithRLock(self):
287 lock = threading.RLock()
288 self.boilerPlate(lock, lock._is_owned)
289
290 def testWithCondition(self):
291 lock = threading.Condition()
292 def locked():
293 return lock._is_owned()
294 self.boilerPlate(lock, locked)
295
296 def testWithSemaphore(self):
297 lock = threading.Semaphore()
298 def locked():
299 if lock.acquire(False):
300 lock.release()
301 return False
302 else:
303 return True
304 self.boilerPlate(lock, locked)
305
306 def testWithBoundedSemaphore(self):
307 lock = threading.BoundedSemaphore()
308 def locked():
309 if lock.acquire(False):
310 lock.release()
311 return False
312 else:
313 return True
314 self.boilerPlate(lock, locked)
315
Michael Foordb3a89842010-06-30 12:17:50 +0000316
317class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000318 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000319 started = False
320 exc = None
321 catch = False
322
323 def __enter__(self):
324 self.started = True
325 return self
326
327 def __exit__(self, *exc):
328 self.exc = exc
329 return self.catch
330
331
332class TestContextDecorator(unittest.TestCase):
333
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000334 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000335 def test_instance_docs(self):
336 # Issue 19330: ensure context manager instances have good docstrings
337 cm_docstring = mycontext.__doc__
338 obj = mycontext()
339 self.assertEqual(obj.__doc__, cm_docstring)
340
Michael Foordb3a89842010-06-30 12:17:50 +0000341 def test_contextdecorator(self):
342 context = mycontext()
343 with context as result:
344 self.assertIs(result, context)
345 self.assertTrue(context.started)
346
347 self.assertEqual(context.exc, (None, None, None))
348
349
350 def test_contextdecorator_with_exception(self):
351 context = mycontext()
352
Ezio Melottied3a7d22010-12-01 02:32:32 +0000353 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000354 with context:
355 raise NameError('foo')
356 self.assertIsNotNone(context.exc)
357 self.assertIs(context.exc[0], NameError)
358
359 context = mycontext()
360 context.catch = True
361 with context:
362 raise NameError('foo')
363 self.assertIsNotNone(context.exc)
364 self.assertIs(context.exc[0], NameError)
365
366
367 def test_decorator(self):
368 context = mycontext()
369
370 @context
371 def test():
372 self.assertIsNone(context.exc)
373 self.assertTrue(context.started)
374 test()
375 self.assertEqual(context.exc, (None, None, None))
376
377
378 def test_decorator_with_exception(self):
379 context = mycontext()
380
381 @context
382 def test():
383 self.assertIsNone(context.exc)
384 self.assertTrue(context.started)
385 raise NameError('foo')
386
Ezio Melottied3a7d22010-12-01 02:32:32 +0000387 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000388 test()
389 self.assertIsNotNone(context.exc)
390 self.assertIs(context.exc[0], NameError)
391
392
393 def test_decorating_method(self):
394 context = mycontext()
395
396 class Test(object):
397
398 @context
399 def method(self, a, b, c=None):
400 self.a = a
401 self.b = b
402 self.c = c
403
404 # these tests are for argument passing when used as a decorator
405 test = Test()
406 test.method(1, 2)
407 self.assertEqual(test.a, 1)
408 self.assertEqual(test.b, 2)
409 self.assertEqual(test.c, None)
410
411 test = Test()
412 test.method('a', 'b', 'c')
413 self.assertEqual(test.a, 'a')
414 self.assertEqual(test.b, 'b')
415 self.assertEqual(test.c, 'c')
416
417 test = Test()
418 test.method(a=1, b=2)
419 self.assertEqual(test.a, 1)
420 self.assertEqual(test.b, 2)
421
422
423 def test_typo_enter(self):
424 class mycontext(ContextDecorator):
425 def __unter__(self):
426 pass
427 def __exit__(self, *exc):
428 pass
429
430 with self.assertRaises(AttributeError):
431 with mycontext():
432 pass
433
434
435 def test_typo_exit(self):
436 class mycontext(ContextDecorator):
437 def __enter__(self):
438 pass
439 def __uxit__(self, *exc):
440 pass
441
442 with self.assertRaises(AttributeError):
443 with mycontext():
444 pass
445
446
447 def test_contextdecorator_as_mixin(self):
448 class somecontext(object):
449 started = False
450 exc = None
451
452 def __enter__(self):
453 self.started = True
454 return self
455
456 def __exit__(self, *exc):
457 self.exc = exc
458
459 class mycontext(somecontext, ContextDecorator):
460 pass
461
462 context = mycontext()
463 @context
464 def test():
465 self.assertIsNone(context.exc)
466 self.assertTrue(context.started)
467 test()
468 self.assertEqual(context.exc, (None, None, None))
469
470
471 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000472 @contextmanager
473 def woohoo(y):
474 state.append(y)
475 yield
476 state.append(999)
477
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000478 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000479 @woohoo(1)
480 def test(x):
481 self.assertEqual(state, [1])
482 state.append(x)
483 test('something')
484 self.assertEqual(state, [1, 'something', 999])
485
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000486 # Issue #11647: Ensure the decorated function is 'reusable'
487 state = []
488 test('something else')
489 self.assertEqual(state, [1, 'something else', 999])
490
Michael Foordb3a89842010-06-30 12:17:50 +0000491
Nick Coghlan3267a302012-05-21 22:54:43 +1000492class TestExitStack(unittest.TestCase):
493
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000494 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000495 def test_instance_docs(self):
496 # Issue 19330: ensure context manager instances have good docstrings
497 cm_docstring = ExitStack.__doc__
498 obj = ExitStack()
499 self.assertEqual(obj.__doc__, cm_docstring)
500
Nick Coghlan3267a302012-05-21 22:54:43 +1000501 def test_no_resources(self):
502 with ExitStack():
503 pass
504
505 def test_callback(self):
506 expected = [
507 ((), {}),
508 ((1,), {}),
509 ((1,2), {}),
510 ((), dict(example=1)),
511 ((1,), dict(example=1)),
512 ((1,2), dict(example=1)),
513 ]
514 result = []
515 def _exit(*args, **kwds):
516 """Test metadata propagation"""
517 result.append((args, kwds))
518 with ExitStack() as stack:
519 for args, kwds in reversed(expected):
520 if args and kwds:
521 f = stack.callback(_exit, *args, **kwds)
522 elif args:
523 f = stack.callback(_exit, *args)
524 elif kwds:
525 f = stack.callback(_exit, **kwds)
526 else:
527 f = stack.callback(_exit)
528 self.assertIs(f, _exit)
529 for wrapper in stack._exit_callbacks:
530 self.assertIs(wrapper.__wrapped__, _exit)
531 self.assertNotEqual(wrapper.__name__, _exit.__name__)
532 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
533 self.assertEqual(result, expected)
534
535 def test_push(self):
536 exc_raised = ZeroDivisionError
537 def _expect_exc(exc_type, exc, exc_tb):
538 self.assertIs(exc_type, exc_raised)
539 def _suppress_exc(*exc_details):
540 return True
541 def _expect_ok(exc_type, exc, exc_tb):
542 self.assertIsNone(exc_type)
543 self.assertIsNone(exc)
544 self.assertIsNone(exc_tb)
545 class ExitCM(object):
546 def __init__(self, check_exc):
547 self.check_exc = check_exc
548 def __enter__(self):
549 self.fail("Should not be called!")
550 def __exit__(self, *exc_details):
551 self.check_exc(*exc_details)
552 with ExitStack() as stack:
553 stack.push(_expect_ok)
554 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
555 cm = ExitCM(_expect_ok)
556 stack.push(cm)
557 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
558 stack.push(_suppress_exc)
559 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
560 cm = ExitCM(_expect_exc)
561 stack.push(cm)
562 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
563 stack.push(_expect_exc)
564 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
565 stack.push(_expect_exc)
566 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
567 1/0
568
569 def test_enter_context(self):
570 class TestCM(object):
571 def __enter__(self):
572 result.append(1)
573 def __exit__(self, *exc_details):
574 result.append(3)
575
576 result = []
577 cm = TestCM()
578 with ExitStack() as stack:
579 @stack.callback # Registered first => cleaned up last
580 def _exit():
581 result.append(4)
582 self.assertIsNotNone(_exit)
583 stack.enter_context(cm)
584 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
585 result.append(2)
586 self.assertEqual(result, [1, 2, 3, 4])
587
588 def test_close(self):
589 result = []
590 with ExitStack() as stack:
591 @stack.callback
592 def _exit():
593 result.append(1)
594 self.assertIsNotNone(_exit)
595 stack.close()
596 result.append(2)
597 self.assertEqual(result, [1, 2])
598
599 def test_pop_all(self):
600 result = []
601 with ExitStack() as stack:
602 @stack.callback
603 def _exit():
604 result.append(3)
605 self.assertIsNotNone(_exit)
606 new_stack = stack.pop_all()
607 result.append(1)
608 result.append(2)
609 new_stack.close()
610 self.assertEqual(result, [1, 2, 3])
611
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000612 def test_exit_raise(self):
613 with self.assertRaises(ZeroDivisionError):
614 with ExitStack() as stack:
615 stack.push(lambda *exc: False)
616 1/0
617
618 def test_exit_suppress(self):
619 with ExitStack() as stack:
620 stack.push(lambda *exc: True)
621 1/0
622
623 def test_exit_exception_chaining_reference(self):
624 # Sanity check to make sure that ExitStack chaining matches
625 # actual nested with statements
626 class RaiseExc:
627 def __init__(self, exc):
628 self.exc = exc
629 def __enter__(self):
630 return self
631 def __exit__(self, *exc_details):
632 raise self.exc
633
Nick Coghlan77452fc2012-06-01 22:48:32 +1000634 class RaiseExcWithContext:
635 def __init__(self, outer, inner):
636 self.outer = outer
637 self.inner = inner
638 def __enter__(self):
639 return self
640 def __exit__(self, *exc_details):
641 try:
642 raise self.inner
643 except:
644 raise self.outer
645
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000646 class SuppressExc:
647 def __enter__(self):
648 return self
649 def __exit__(self, *exc_details):
650 type(self).saved_details = exc_details
651 return True
652
653 try:
654 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000655 with RaiseExcWithContext(KeyError, AttributeError):
656 with SuppressExc():
657 with RaiseExc(ValueError):
658 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000659 except IndexError as exc:
660 self.assertIsInstance(exc.__context__, KeyError)
661 self.assertIsInstance(exc.__context__.__context__, AttributeError)
662 # Inner exceptions were suppressed
663 self.assertIsNone(exc.__context__.__context__.__context__)
664 else:
665 self.fail("Expected IndexError, but no exception was raised")
666 # Check the inner exceptions
667 inner_exc = SuppressExc.saved_details[1]
668 self.assertIsInstance(inner_exc, ValueError)
669 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
670
671 def test_exit_exception_chaining(self):
672 # Ensure exception chaining matches the reference behaviour
673 def raise_exc(exc):
674 raise exc
675
676 saved_details = None
677 def suppress_exc(*exc_details):
678 nonlocal saved_details
679 saved_details = exc_details
680 return True
681
682 try:
683 with ExitStack() as stack:
684 stack.callback(raise_exc, IndexError)
685 stack.callback(raise_exc, KeyError)
686 stack.callback(raise_exc, AttributeError)
687 stack.push(suppress_exc)
688 stack.callback(raise_exc, ValueError)
689 1 / 0
690 except IndexError as exc:
691 self.assertIsInstance(exc.__context__, KeyError)
692 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000693 # Inner exceptions were suppressed
694 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000695 else:
696 self.fail("Expected IndexError, but no exception was raised")
697 # Check the inner exceptions
698 inner_exc = saved_details[1]
699 self.assertIsInstance(inner_exc, ValueError)
700 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
701
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000702 def test_exit_exception_non_suppressing(self):
703 # http://bugs.python.org/issue19092
704 def raise_exc(exc):
705 raise exc
706
707 def suppress_exc(*exc_details):
708 return True
709
710 try:
711 with ExitStack() as stack:
712 stack.callback(lambda: None)
713 stack.callback(raise_exc, IndexError)
714 except Exception as exc:
715 self.assertIsInstance(exc, IndexError)
716 else:
717 self.fail("Expected IndexError, but no exception was raised")
718
719 try:
720 with ExitStack() as stack:
721 stack.callback(raise_exc, KeyError)
722 stack.push(suppress_exc)
723 stack.callback(raise_exc, IndexError)
724 except Exception as exc:
725 self.assertIsInstance(exc, KeyError)
726 else:
727 self.fail("Expected KeyError, but no exception was raised")
728
Nick Coghlan09761e72014-01-22 22:24:46 +1000729 def test_exit_exception_with_correct_context(self):
730 # http://bugs.python.org/issue20317
731 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000732 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000733 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000734 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000735 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000736 raise exc
737
738 exc1 = Exception(1)
739 exc2 = Exception(2)
740 exc3 = Exception(3)
741 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000742
743 # The contextmanager already fixes the context, so prior to the
744 # fix, ExitStack would try to fix it *again* and get into an
745 # infinite self-referential loop
746 try:
747 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000748 stack.enter_context(gets_the_context_right(exc4))
749 stack.enter_context(gets_the_context_right(exc3))
750 stack.enter_context(gets_the_context_right(exc2))
751 raise exc1
752 except Exception as exc:
753 self.assertIs(exc, exc4)
754 self.assertIs(exc.__context__, exc3)
755 self.assertIs(exc.__context__.__context__, exc2)
756 self.assertIs(exc.__context__.__context__.__context__, exc1)
757 self.assertIsNone(
758 exc.__context__.__context__.__context__.__context__)
759
760 def test_exit_exception_with_existing_context(self):
761 # Addresses a lack of test coverage discovered after checking in a
762 # fix for issue 20317 that still contained debugging code.
763 def raise_nested(inner_exc, outer_exc):
764 try:
765 raise inner_exc
766 finally:
767 raise outer_exc
768 exc1 = Exception(1)
769 exc2 = Exception(2)
770 exc3 = Exception(3)
771 exc4 = Exception(4)
772 exc5 = Exception(5)
773 try:
774 with ExitStack() as stack:
775 stack.callback(raise_nested, exc4, exc5)
776 stack.callback(raise_nested, exc2, exc3)
777 raise exc1
778 except Exception as exc:
779 self.assertIs(exc, exc5)
780 self.assertIs(exc.__context__, exc4)
781 self.assertIs(exc.__context__.__context__, exc3)
782 self.assertIs(exc.__context__.__context__.__context__, exc2)
783 self.assertIs(
784 exc.__context__.__context__.__context__.__context__, exc1)
785 self.assertIsNone(
786 exc.__context__.__context__.__context__.__context__.__context__)
787
Nick Coghlan09761e72014-01-22 22:24:46 +1000788
789
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000790 def test_body_exception_suppress(self):
791 def suppress_exc(*exc_details):
792 return True
793 try:
794 with ExitStack() as stack:
795 stack.push(suppress_exc)
796 1/0
797 except IndexError as exc:
798 self.fail("Expected no exception, got IndexError")
799
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000800 def test_exit_exception_chaining_suppress(self):
801 with ExitStack() as stack:
802 stack.push(lambda *exc: True)
803 stack.push(lambda *exc: 1/0)
804 stack.push(lambda *exc: {}[1])
805
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000806 def test_excessive_nesting(self):
807 # The original implementation would die with RecursionError here
808 with ExitStack() as stack:
809 for i in range(10000):
810 stack.callback(int)
811
Nick Coghlan3267a302012-05-21 22:54:43 +1000812 def test_instance_bypass(self):
813 class Example(object): pass
814 cm = Example()
815 cm.__exit__ = object()
816 stack = ExitStack()
817 self.assertRaises(AttributeError, stack.enter_context, cm)
818 stack.push(cm)
819 self.assertIs(stack._exit_callbacks[-1], cm)
820
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700821 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300822 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700823 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300824 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700825
826 @contextmanager
827 def second():
828 try:
829 yield 1
830 except Exception as exc:
831 raise UniqueException("new exception") from exc
832
833 @contextmanager
834 def first():
835 try:
836 yield 1
837 except Exception as exc:
838 raise exc
839
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300840 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700841 # handler which chain raised a new UniqueException.
842 with self.assertRaises(UniqueException) as err_ctx:
843 with ExitStack() as es_ctx:
844 es_ctx.enter_context(second())
845 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300846 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700847
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300848 exc = err_ctx.exception
849 self.assertIsInstance(exc, UniqueException)
850 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
851 self.assertIsNone(exc.__context__.__context__)
852 self.assertIsNone(exc.__context__.__cause__)
853 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700854
Berker Peksagbb44fe02014-11-28 23:28:06 +0200855
856class TestRedirectStream:
857
858 redirect_stream = None
859 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700860
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000861 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000862 def test_instance_docs(self):
863 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200864 cm_docstring = self.redirect_stream.__doc__
865 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000866 self.assertEqual(obj.__doc__, cm_docstring)
867
Nick Coghlan8e113b42013-11-03 17:00:51 +1000868 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200869 orig_stdout = getattr(sys, self.orig_stream)
870 self.redirect_stream(None)
871 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000872
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700873 def test_redirect_to_string_io(self):
874 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000875 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200876 orig_stdout = getattr(sys, self.orig_stream)
877 with self.redirect_stream(f):
878 print(msg, file=getattr(sys, self.orig_stream))
879 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000880 s = f.getvalue().strip()
881 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000882
Nick Coghlan8608d262013-10-20 00:30:51 +1000883 def test_enter_result_is_target(self):
884 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200885 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000886 self.assertIs(enter_result, f)
887
888 def test_cm_is_reusable(self):
889 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200890 write_to_f = self.redirect_stream(f)
891 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000892 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200893 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000894 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200895 print("World!", file=getattr(sys, self.orig_stream))
896 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000897 s = f.getvalue()
898 self.assertEqual(s, "Hello World!\n")
899
Nick Coghlan8e113b42013-11-03 17:00:51 +1000900 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000901 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200902 write_to_f = self.redirect_stream(f)
903 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000904 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200905 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000906 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200907 print("World!", file=getattr(sys, self.orig_stream))
908 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000909 s = f.getvalue()
910 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000911
912
Berker Peksagbb44fe02014-11-28 23:28:06 +0200913class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
914
915 redirect_stream = redirect_stdout
916 orig_stream = "stdout"
917
918
919class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
920
921 redirect_stream = redirect_stderr
922 orig_stream = "stderr"
923
924
Nick Coghlan240f86d2013-10-17 23:40:57 +1000925class TestSuppress(unittest.TestCase):
926
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000927 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000928 def test_instance_docs(self):
929 # Issue 19330: ensure context manager instances have good docstrings
930 cm_docstring = suppress.__doc__
931 obj = suppress()
932 self.assertEqual(obj.__doc__, cm_docstring)
933
Nick Coghlan8608d262013-10-20 00:30:51 +1000934 def test_no_result_from_enter(self):
935 with suppress(ValueError) as enter_result:
936 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000937
Nick Coghlan8608d262013-10-20 00:30:51 +1000938 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000939 with suppress(ValueError):
940 self.assertEqual(pow(2, 5), 32)
941
942 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000943 with suppress(TypeError):
944 len(5)
945
Nick Coghlan059def52013-10-26 18:08:15 +1000946 def test_exception_hierarchy(self):
947 with suppress(LookupError):
948 'Hello'[50]
949
950 def test_other_exception(self):
951 with self.assertRaises(ZeroDivisionError):
952 with suppress(TypeError):
953 1/0
954
955 def test_no_args(self):
956 with self.assertRaises(ZeroDivisionError):
957 with suppress():
958 1/0
959
Nick Coghlan240f86d2013-10-17 23:40:57 +1000960 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000961 with suppress(ZeroDivisionError, TypeError):
962 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000963 with suppress(ZeroDivisionError, TypeError):
964 len(5)
965
Nick Coghlan8608d262013-10-20 00:30:51 +1000966 def test_cm_is_reentrant(self):
967 ignore_exceptions = suppress(Exception)
968 with ignore_exceptions:
969 pass
970 with ignore_exceptions:
971 len(5)
972 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +1000973 with ignore_exceptions: # Check nested usage
974 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +0000975 outer_continued = True
976 1/0
977 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +1000978
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000979if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400980 unittest.main()