blob: 2301f759d80858e791c6f10d682cbaa45239dee7 [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
Jelle Zijlstra57161aa2017-06-09 08:21:47 -070047 class NoEnter(ManagerFromScratch):
48 __enter__ = None
49
50 self.assertFalse(issubclass(NoEnter, AbstractContextManager))
51
52 class NoExit(ManagerFromScratch):
53 __exit__ = None
54
55 self.assertFalse(issubclass(NoExit, AbstractContextManager))
56
Brett Cannon9e080e02016-04-08 12:15:27 -070057
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000058class ContextManagerTestCase(unittest.TestCase):
59
60 def test_contextmanager_plain(self):
61 state = []
62 @contextmanager
63 def woohoo():
64 state.append(1)
65 yield 42
66 state.append(999)
67 with woohoo() as x:
68 self.assertEqual(state, [1])
69 self.assertEqual(x, 42)
70 state.append(x)
71 self.assertEqual(state, [1, 42, 999])
72
73 def test_contextmanager_finally(self):
74 state = []
75 @contextmanager
76 def woohoo():
77 state.append(1)
78 try:
79 yield 42
80 finally:
81 state.append(999)
Florent Xicluna41fe6152010-04-02 18:52:12 +000082 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000083 with woohoo() as x:
84 self.assertEqual(state, [1])
85 self.assertEqual(x, 42)
86 state.append(x)
87 raise ZeroDivisionError()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000088 self.assertEqual(state, [1, 42, 999])
89
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000090 def test_contextmanager_no_reraise(self):
91 @contextmanager
92 def whee():
93 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000094 ctx = whee()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000095 ctx.__enter__()
96 # Calling __exit__ should not result in an exception
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000097 self.assertFalse(ctx.__exit__(TypeError, TypeError("foo"), None))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000098
99 def test_contextmanager_trap_yield_after_throw(self):
100 @contextmanager
101 def whoo():
102 try:
103 yield
104 except:
105 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +0000106 ctx = whoo()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000107 ctx.__enter__()
108 self.assertRaises(
109 RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
110 )
111
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000112 def test_contextmanager_except(self):
113 state = []
114 @contextmanager
115 def woohoo():
116 state.append(1)
117 try:
118 yield 42
Guido van Rossumb940e112007-01-10 16:19:56 +0000119 except ZeroDivisionError as e:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000120 state.append(e.args[0])
121 self.assertEqual(state, [1, 42, 999])
122 with woohoo() as x:
123 self.assertEqual(state, [1])
124 self.assertEqual(x, 42)
125 state.append(x)
126 raise ZeroDivisionError(999)
127 self.assertEqual(state, [1, 42, 999])
128
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400129 def test_contextmanager_except_stopiter(self):
130 stop_exc = StopIteration('spam')
131 @contextmanager
132 def woohoo():
133 yield
134 try:
Martin Panter7e3a91a2016-02-10 04:40:48 +0000135 with self.assertWarnsRegex(DeprecationWarning,
Yury Selivanov68333392015-05-22 11:16:47 -0400136 "StopIteration"):
137 with woohoo():
138 raise stop_exc
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400139 except Exception as ex:
140 self.assertIs(ex, stop_exc)
141 else:
142 self.fail('StopIteration was suppressed')
143
144 def test_contextmanager_except_pep479(self):
145 code = """\
146from __future__ import generator_stop
147from contextlib import contextmanager
148@contextmanager
149def woohoo():
150 yield
151"""
152 locals = {}
153 exec(code, locals, locals)
154 woohoo = locals['woohoo']
155
156 stop_exc = StopIteration('spam')
157 try:
158 with woohoo():
159 raise stop_exc
160 except Exception as ex:
161 self.assertIs(ex, stop_exc)
162 else:
163 self.fail('StopIteration was suppressed')
164
svelankar00c75e92017-04-11 05:11:13 -0400165 def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
166 @contextmanager
167 def test_issue29692():
168 try:
169 yield
170 except Exception as exc:
171 raise RuntimeError('issue29692:Chained') from exc
172 try:
173 with test_issue29692():
174 raise ZeroDivisionError
175 except Exception as ex:
176 self.assertIs(type(ex), RuntimeError)
177 self.assertEqual(ex.args[0], 'issue29692:Chained')
178 self.assertIsInstance(ex.__cause__, ZeroDivisionError)
179
180 try:
181 with test_issue29692():
182 raise StopIteration('issue29692:Unchained')
183 except Exception as ex:
184 self.assertIs(type(ex), StopIteration)
185 self.assertEqual(ex.args[0], 'issue29692:Unchained')
186 self.assertIsNone(ex.__cause__)
187
R. David Murray378c0cf2010-02-24 01:46:21 +0000188 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000189 def attribs(**kw):
190 def decorate(func):
191 for k,v in kw.items():
192 setattr(func,k,v)
193 return func
194 return decorate
195 @contextmanager
196 @attribs(foo='bar')
197 def baz(spam):
198 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000199 return baz
200
201 def test_contextmanager_attribs(self):
202 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000203 self.assertEqual(baz.__name__,'baz')
204 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000205
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000206 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000207 def test_contextmanager_doc_attrib(self):
208 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000209 self.assertEqual(baz.__doc__, "Whee!")
210
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000211 @support.requires_docstrings
212 def test_instance_docstring_given_cm_docstring(self):
213 baz = self._create_contextmanager_attribs()(None)
214 self.assertEqual(baz.__doc__, "Whee!")
215
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300216 def test_keywords(self):
217 # Ensure no keyword arguments are inhibited
218 @contextmanager
219 def woohoo(self, func, args, kwds):
220 yield (self, func, args, kwds)
221 with woohoo(self=11, func=22, args=33, kwds=44) as target:
222 self.assertEqual(target, (11, 22, 33, 44))
223
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000224
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000225class ClosingTestCase(unittest.TestCase):
226
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000227 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000228 def test_instance_docs(self):
229 # Issue 19330: ensure context manager instances have good docstrings
230 cm_docstring = closing.__doc__
231 obj = closing(None)
232 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000233
234 def test_closing(self):
235 state = []
236 class C:
237 def close(self):
238 state.append(1)
239 x = C()
240 self.assertEqual(state, [])
241 with closing(x) as y:
242 self.assertEqual(x, y)
243 self.assertEqual(state, [1])
244
245 def test_closing_error(self):
246 state = []
247 class C:
248 def close(self):
249 state.append(1)
250 x = C()
251 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000252 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000253 with closing(x) as y:
254 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000255 1 / 0
256 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000257
258class FileContextTestCase(unittest.TestCase):
259
260 def testWithOpen(self):
261 tfn = tempfile.mktemp()
262 try:
263 f = None
264 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000265 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000266 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000267 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000268 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000269 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000270 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000271 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000272 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000273 1 / 0
274 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000275 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000276 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000277
Victor Stinner45df8202010-04-28 22:31:17 +0000278@unittest.skipUnless(threading, 'Threading required for this test.')
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000279class LockContextTestCase(unittest.TestCase):
280
281 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000282 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000283 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000284 self.assertTrue(locked())
285 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000286 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000287 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000288 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000289 1 / 0
290 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000291
292 def testWithLock(self):
293 lock = threading.Lock()
294 self.boilerPlate(lock, lock.locked)
295
296 def testWithRLock(self):
297 lock = threading.RLock()
298 self.boilerPlate(lock, lock._is_owned)
299
300 def testWithCondition(self):
301 lock = threading.Condition()
302 def locked():
303 return lock._is_owned()
304 self.boilerPlate(lock, locked)
305
306 def testWithSemaphore(self):
307 lock = threading.Semaphore()
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
316 def testWithBoundedSemaphore(self):
317 lock = threading.BoundedSemaphore()
318 def locked():
319 if lock.acquire(False):
320 lock.release()
321 return False
322 else:
323 return True
324 self.boilerPlate(lock, locked)
325
Michael Foordb3a89842010-06-30 12:17:50 +0000326
327class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000328 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000329 started = False
330 exc = None
331 catch = False
332
333 def __enter__(self):
334 self.started = True
335 return self
336
337 def __exit__(self, *exc):
338 self.exc = exc
339 return self.catch
340
341
342class TestContextDecorator(unittest.TestCase):
343
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000344 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000345 def test_instance_docs(self):
346 # Issue 19330: ensure context manager instances have good docstrings
347 cm_docstring = mycontext.__doc__
348 obj = mycontext()
349 self.assertEqual(obj.__doc__, cm_docstring)
350
Michael Foordb3a89842010-06-30 12:17:50 +0000351 def test_contextdecorator(self):
352 context = mycontext()
353 with context as result:
354 self.assertIs(result, context)
355 self.assertTrue(context.started)
356
357 self.assertEqual(context.exc, (None, None, None))
358
359
360 def test_contextdecorator_with_exception(self):
361 context = mycontext()
362
Ezio Melottied3a7d22010-12-01 02:32:32 +0000363 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000364 with context:
365 raise NameError('foo')
366 self.assertIsNotNone(context.exc)
367 self.assertIs(context.exc[0], NameError)
368
369 context = mycontext()
370 context.catch = True
371 with context:
372 raise NameError('foo')
373 self.assertIsNotNone(context.exc)
374 self.assertIs(context.exc[0], NameError)
375
376
377 def test_decorator(self):
378 context = mycontext()
379
380 @context
381 def test():
382 self.assertIsNone(context.exc)
383 self.assertTrue(context.started)
384 test()
385 self.assertEqual(context.exc, (None, None, None))
386
387
388 def test_decorator_with_exception(self):
389 context = mycontext()
390
391 @context
392 def test():
393 self.assertIsNone(context.exc)
394 self.assertTrue(context.started)
395 raise NameError('foo')
396
Ezio Melottied3a7d22010-12-01 02:32:32 +0000397 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000398 test()
399 self.assertIsNotNone(context.exc)
400 self.assertIs(context.exc[0], NameError)
401
402
403 def test_decorating_method(self):
404 context = mycontext()
405
406 class Test(object):
407
408 @context
409 def method(self, a, b, c=None):
410 self.a = a
411 self.b = b
412 self.c = c
413
414 # these tests are for argument passing when used as a decorator
415 test = Test()
416 test.method(1, 2)
417 self.assertEqual(test.a, 1)
418 self.assertEqual(test.b, 2)
419 self.assertEqual(test.c, None)
420
421 test = Test()
422 test.method('a', 'b', 'c')
423 self.assertEqual(test.a, 'a')
424 self.assertEqual(test.b, 'b')
425 self.assertEqual(test.c, 'c')
426
427 test = Test()
428 test.method(a=1, b=2)
429 self.assertEqual(test.a, 1)
430 self.assertEqual(test.b, 2)
431
432
433 def test_typo_enter(self):
434 class mycontext(ContextDecorator):
435 def __unter__(self):
436 pass
437 def __exit__(self, *exc):
438 pass
439
440 with self.assertRaises(AttributeError):
441 with mycontext():
442 pass
443
444
445 def test_typo_exit(self):
446 class mycontext(ContextDecorator):
447 def __enter__(self):
448 pass
449 def __uxit__(self, *exc):
450 pass
451
452 with self.assertRaises(AttributeError):
453 with mycontext():
454 pass
455
456
457 def test_contextdecorator_as_mixin(self):
458 class somecontext(object):
459 started = False
460 exc = None
461
462 def __enter__(self):
463 self.started = True
464 return self
465
466 def __exit__(self, *exc):
467 self.exc = exc
468
469 class mycontext(somecontext, ContextDecorator):
470 pass
471
472 context = mycontext()
473 @context
474 def test():
475 self.assertIsNone(context.exc)
476 self.assertTrue(context.started)
477 test()
478 self.assertEqual(context.exc, (None, None, None))
479
480
481 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000482 @contextmanager
483 def woohoo(y):
484 state.append(y)
485 yield
486 state.append(999)
487
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000488 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000489 @woohoo(1)
490 def test(x):
491 self.assertEqual(state, [1])
492 state.append(x)
493 test('something')
494 self.assertEqual(state, [1, 'something', 999])
495
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000496 # Issue #11647: Ensure the decorated function is 'reusable'
497 state = []
498 test('something else')
499 self.assertEqual(state, [1, 'something else', 999])
500
Michael Foordb3a89842010-06-30 12:17:50 +0000501
Nick Coghlan3267a302012-05-21 22:54:43 +1000502class TestExitStack(unittest.TestCase):
503
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000504 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000505 def test_instance_docs(self):
506 # Issue 19330: ensure context manager instances have good docstrings
507 cm_docstring = ExitStack.__doc__
508 obj = ExitStack()
509 self.assertEqual(obj.__doc__, cm_docstring)
510
Nick Coghlan3267a302012-05-21 22:54:43 +1000511 def test_no_resources(self):
512 with ExitStack():
513 pass
514
515 def test_callback(self):
516 expected = [
517 ((), {}),
518 ((1,), {}),
519 ((1,2), {}),
520 ((), dict(example=1)),
521 ((1,), dict(example=1)),
522 ((1,2), dict(example=1)),
523 ]
524 result = []
525 def _exit(*args, **kwds):
526 """Test metadata propagation"""
527 result.append((args, kwds))
528 with ExitStack() as stack:
529 for args, kwds in reversed(expected):
530 if args and kwds:
531 f = stack.callback(_exit, *args, **kwds)
532 elif args:
533 f = stack.callback(_exit, *args)
534 elif kwds:
535 f = stack.callback(_exit, **kwds)
536 else:
537 f = stack.callback(_exit)
538 self.assertIs(f, _exit)
539 for wrapper in stack._exit_callbacks:
540 self.assertIs(wrapper.__wrapped__, _exit)
541 self.assertNotEqual(wrapper.__name__, _exit.__name__)
542 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
543 self.assertEqual(result, expected)
544
545 def test_push(self):
546 exc_raised = ZeroDivisionError
547 def _expect_exc(exc_type, exc, exc_tb):
548 self.assertIs(exc_type, exc_raised)
549 def _suppress_exc(*exc_details):
550 return True
551 def _expect_ok(exc_type, exc, exc_tb):
552 self.assertIsNone(exc_type)
553 self.assertIsNone(exc)
554 self.assertIsNone(exc_tb)
555 class ExitCM(object):
556 def __init__(self, check_exc):
557 self.check_exc = check_exc
558 def __enter__(self):
559 self.fail("Should not be called!")
560 def __exit__(self, *exc_details):
561 self.check_exc(*exc_details)
562 with ExitStack() as stack:
563 stack.push(_expect_ok)
564 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
565 cm = ExitCM(_expect_ok)
566 stack.push(cm)
567 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
568 stack.push(_suppress_exc)
569 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
570 cm = ExitCM(_expect_exc)
571 stack.push(cm)
572 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
573 stack.push(_expect_exc)
574 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
575 stack.push(_expect_exc)
576 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
577 1/0
578
579 def test_enter_context(self):
580 class TestCM(object):
581 def __enter__(self):
582 result.append(1)
583 def __exit__(self, *exc_details):
584 result.append(3)
585
586 result = []
587 cm = TestCM()
588 with ExitStack() as stack:
589 @stack.callback # Registered first => cleaned up last
590 def _exit():
591 result.append(4)
592 self.assertIsNotNone(_exit)
593 stack.enter_context(cm)
594 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
595 result.append(2)
596 self.assertEqual(result, [1, 2, 3, 4])
597
598 def test_close(self):
599 result = []
600 with ExitStack() as stack:
601 @stack.callback
602 def _exit():
603 result.append(1)
604 self.assertIsNotNone(_exit)
605 stack.close()
606 result.append(2)
607 self.assertEqual(result, [1, 2])
608
609 def test_pop_all(self):
610 result = []
611 with ExitStack() as stack:
612 @stack.callback
613 def _exit():
614 result.append(3)
615 self.assertIsNotNone(_exit)
616 new_stack = stack.pop_all()
617 result.append(1)
618 result.append(2)
619 new_stack.close()
620 self.assertEqual(result, [1, 2, 3])
621
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000622 def test_exit_raise(self):
623 with self.assertRaises(ZeroDivisionError):
624 with ExitStack() as stack:
625 stack.push(lambda *exc: False)
626 1/0
627
628 def test_exit_suppress(self):
629 with ExitStack() as stack:
630 stack.push(lambda *exc: True)
631 1/0
632
633 def test_exit_exception_chaining_reference(self):
634 # Sanity check to make sure that ExitStack chaining matches
635 # actual nested with statements
636 class RaiseExc:
637 def __init__(self, exc):
638 self.exc = exc
639 def __enter__(self):
640 return self
641 def __exit__(self, *exc_details):
642 raise self.exc
643
Nick Coghlan77452fc2012-06-01 22:48:32 +1000644 class RaiseExcWithContext:
645 def __init__(self, outer, inner):
646 self.outer = outer
647 self.inner = inner
648 def __enter__(self):
649 return self
650 def __exit__(self, *exc_details):
651 try:
652 raise self.inner
653 except:
654 raise self.outer
655
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000656 class SuppressExc:
657 def __enter__(self):
658 return self
659 def __exit__(self, *exc_details):
660 type(self).saved_details = exc_details
661 return True
662
663 try:
664 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000665 with RaiseExcWithContext(KeyError, AttributeError):
666 with SuppressExc():
667 with RaiseExc(ValueError):
668 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000669 except IndexError as exc:
670 self.assertIsInstance(exc.__context__, KeyError)
671 self.assertIsInstance(exc.__context__.__context__, AttributeError)
672 # Inner exceptions were suppressed
673 self.assertIsNone(exc.__context__.__context__.__context__)
674 else:
675 self.fail("Expected IndexError, but no exception was raised")
676 # Check the inner exceptions
677 inner_exc = SuppressExc.saved_details[1]
678 self.assertIsInstance(inner_exc, ValueError)
679 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
680
681 def test_exit_exception_chaining(self):
682 # Ensure exception chaining matches the reference behaviour
683 def raise_exc(exc):
684 raise exc
685
686 saved_details = None
687 def suppress_exc(*exc_details):
688 nonlocal saved_details
689 saved_details = exc_details
690 return True
691
692 try:
693 with ExitStack() as stack:
694 stack.callback(raise_exc, IndexError)
695 stack.callback(raise_exc, KeyError)
696 stack.callback(raise_exc, AttributeError)
697 stack.push(suppress_exc)
698 stack.callback(raise_exc, ValueError)
699 1 / 0
700 except IndexError as exc:
701 self.assertIsInstance(exc.__context__, KeyError)
702 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000703 # Inner exceptions were suppressed
704 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000705 else:
706 self.fail("Expected IndexError, but no exception was raised")
707 # Check the inner exceptions
708 inner_exc = saved_details[1]
709 self.assertIsInstance(inner_exc, ValueError)
710 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
711
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000712 def test_exit_exception_non_suppressing(self):
713 # http://bugs.python.org/issue19092
714 def raise_exc(exc):
715 raise exc
716
717 def suppress_exc(*exc_details):
718 return True
719
720 try:
721 with ExitStack() as stack:
722 stack.callback(lambda: None)
723 stack.callback(raise_exc, IndexError)
724 except Exception as exc:
725 self.assertIsInstance(exc, IndexError)
726 else:
727 self.fail("Expected IndexError, but no exception was raised")
728
729 try:
730 with ExitStack() as stack:
731 stack.callback(raise_exc, KeyError)
732 stack.push(suppress_exc)
733 stack.callback(raise_exc, IndexError)
734 except Exception as exc:
735 self.assertIsInstance(exc, KeyError)
736 else:
737 self.fail("Expected KeyError, but no exception was raised")
738
Nick Coghlan09761e72014-01-22 22:24:46 +1000739 def test_exit_exception_with_correct_context(self):
740 # http://bugs.python.org/issue20317
741 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000742 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000743 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000744 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000745 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000746 raise exc
747
748 exc1 = Exception(1)
749 exc2 = Exception(2)
750 exc3 = Exception(3)
751 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000752
753 # The contextmanager already fixes the context, so prior to the
754 # fix, ExitStack would try to fix it *again* and get into an
755 # infinite self-referential loop
756 try:
757 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000758 stack.enter_context(gets_the_context_right(exc4))
759 stack.enter_context(gets_the_context_right(exc3))
760 stack.enter_context(gets_the_context_right(exc2))
761 raise exc1
762 except Exception as exc:
763 self.assertIs(exc, exc4)
764 self.assertIs(exc.__context__, exc3)
765 self.assertIs(exc.__context__.__context__, exc2)
766 self.assertIs(exc.__context__.__context__.__context__, exc1)
767 self.assertIsNone(
768 exc.__context__.__context__.__context__.__context__)
769
770 def test_exit_exception_with_existing_context(self):
771 # Addresses a lack of test coverage discovered after checking in a
772 # fix for issue 20317 that still contained debugging code.
773 def raise_nested(inner_exc, outer_exc):
774 try:
775 raise inner_exc
776 finally:
777 raise outer_exc
778 exc1 = Exception(1)
779 exc2 = Exception(2)
780 exc3 = Exception(3)
781 exc4 = Exception(4)
782 exc5 = Exception(5)
783 try:
784 with ExitStack() as stack:
785 stack.callback(raise_nested, exc4, exc5)
786 stack.callback(raise_nested, exc2, exc3)
787 raise exc1
788 except Exception as exc:
789 self.assertIs(exc, exc5)
790 self.assertIs(exc.__context__, exc4)
791 self.assertIs(exc.__context__.__context__, exc3)
792 self.assertIs(exc.__context__.__context__.__context__, exc2)
793 self.assertIs(
794 exc.__context__.__context__.__context__.__context__, exc1)
795 self.assertIsNone(
796 exc.__context__.__context__.__context__.__context__.__context__)
797
Nick Coghlan09761e72014-01-22 22:24:46 +1000798
799
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000800 def test_body_exception_suppress(self):
801 def suppress_exc(*exc_details):
802 return True
803 try:
804 with ExitStack() as stack:
805 stack.push(suppress_exc)
806 1/0
807 except IndexError as exc:
808 self.fail("Expected no exception, got IndexError")
809
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000810 def test_exit_exception_chaining_suppress(self):
811 with ExitStack() as stack:
812 stack.push(lambda *exc: True)
813 stack.push(lambda *exc: 1/0)
814 stack.push(lambda *exc: {}[1])
815
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000816 def test_excessive_nesting(self):
817 # The original implementation would die with RecursionError here
818 with ExitStack() as stack:
819 for i in range(10000):
820 stack.callback(int)
821
Nick Coghlan3267a302012-05-21 22:54:43 +1000822 def test_instance_bypass(self):
823 class Example(object): pass
824 cm = Example()
825 cm.__exit__ = object()
826 stack = ExitStack()
827 self.assertRaises(AttributeError, stack.enter_context, cm)
828 stack.push(cm)
829 self.assertIs(stack._exit_callbacks[-1], cm)
830
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700831 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300832 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700833 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300834 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700835
836 @contextmanager
837 def second():
838 try:
839 yield 1
840 except Exception as exc:
841 raise UniqueException("new exception") from exc
842
843 @contextmanager
844 def first():
845 try:
846 yield 1
847 except Exception as exc:
848 raise exc
849
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300850 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700851 # handler which chain raised a new UniqueException.
852 with self.assertRaises(UniqueException) as err_ctx:
853 with ExitStack() as es_ctx:
854 es_ctx.enter_context(second())
855 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300856 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700857
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300858 exc = err_ctx.exception
859 self.assertIsInstance(exc, UniqueException)
860 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
861 self.assertIsNone(exc.__context__.__context__)
862 self.assertIsNone(exc.__context__.__cause__)
863 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700864
Berker Peksagbb44fe02014-11-28 23:28:06 +0200865
866class TestRedirectStream:
867
868 redirect_stream = None
869 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700870
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000871 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000872 def test_instance_docs(self):
873 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200874 cm_docstring = self.redirect_stream.__doc__
875 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000876 self.assertEqual(obj.__doc__, cm_docstring)
877
Nick Coghlan8e113b42013-11-03 17:00:51 +1000878 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200879 orig_stdout = getattr(sys, self.orig_stream)
880 self.redirect_stream(None)
881 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000882
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700883 def test_redirect_to_string_io(self):
884 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000885 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200886 orig_stdout = getattr(sys, self.orig_stream)
887 with self.redirect_stream(f):
888 print(msg, file=getattr(sys, self.orig_stream))
889 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000890 s = f.getvalue().strip()
891 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000892
Nick Coghlan8608d262013-10-20 00:30:51 +1000893 def test_enter_result_is_target(self):
894 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200895 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000896 self.assertIs(enter_result, f)
897
898 def test_cm_is_reusable(self):
899 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200900 write_to_f = self.redirect_stream(f)
901 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000902 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200903 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000904 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200905 print("World!", file=getattr(sys, self.orig_stream))
906 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000907 s = f.getvalue()
908 self.assertEqual(s, "Hello World!\n")
909
Nick Coghlan8e113b42013-11-03 17:00:51 +1000910 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000911 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200912 write_to_f = self.redirect_stream(f)
913 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000914 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200915 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000916 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200917 print("World!", file=getattr(sys, self.orig_stream))
918 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000919 s = f.getvalue()
920 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000921
922
Berker Peksagbb44fe02014-11-28 23:28:06 +0200923class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
924
925 redirect_stream = redirect_stdout
926 orig_stream = "stdout"
927
928
929class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
930
931 redirect_stream = redirect_stderr
932 orig_stream = "stderr"
933
934
Nick Coghlan240f86d2013-10-17 23:40:57 +1000935class TestSuppress(unittest.TestCase):
936
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000937 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000938 def test_instance_docs(self):
939 # Issue 19330: ensure context manager instances have good docstrings
940 cm_docstring = suppress.__doc__
941 obj = suppress()
942 self.assertEqual(obj.__doc__, cm_docstring)
943
Nick Coghlan8608d262013-10-20 00:30:51 +1000944 def test_no_result_from_enter(self):
945 with suppress(ValueError) as enter_result:
946 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000947
Nick Coghlan8608d262013-10-20 00:30:51 +1000948 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000949 with suppress(ValueError):
950 self.assertEqual(pow(2, 5), 32)
951
952 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000953 with suppress(TypeError):
954 len(5)
955
Nick Coghlan059def52013-10-26 18:08:15 +1000956 def test_exception_hierarchy(self):
957 with suppress(LookupError):
958 'Hello'[50]
959
960 def test_other_exception(self):
961 with self.assertRaises(ZeroDivisionError):
962 with suppress(TypeError):
963 1/0
964
965 def test_no_args(self):
966 with self.assertRaises(ZeroDivisionError):
967 with suppress():
968 1/0
969
Nick Coghlan240f86d2013-10-17 23:40:57 +1000970 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000971 with suppress(ZeroDivisionError, TypeError):
972 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000973 with suppress(ZeroDivisionError, TypeError):
974 len(5)
975
Nick Coghlan8608d262013-10-20 00:30:51 +1000976 def test_cm_is_reentrant(self):
977 ignore_exceptions = suppress(Exception)
978 with ignore_exceptions:
979 pass
980 with ignore_exceptions:
981 len(5)
982 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +1000983 with ignore_exceptions: # Check nested usage
984 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +0000985 outer_continued = True
986 1/0
987 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +1000988
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000989if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400990 unittest.main()