blob: 516403ef655b9ed0c61f23b9963793e70ef000ae [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
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000015class ContextManagerTestCase(unittest.TestCase):
16
17 def test_contextmanager_plain(self):
18 state = []
19 @contextmanager
20 def woohoo():
21 state.append(1)
22 yield 42
23 state.append(999)
24 with woohoo() as x:
25 self.assertEqual(state, [1])
26 self.assertEqual(x, 42)
27 state.append(x)
28 self.assertEqual(state, [1, 42, 999])
29
30 def test_contextmanager_finally(self):
31 state = []
32 @contextmanager
33 def woohoo():
34 state.append(1)
35 try:
36 yield 42
37 finally:
38 state.append(999)
Florent Xicluna41fe6152010-04-02 18:52:12 +000039 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000040 with woohoo() as x:
41 self.assertEqual(state, [1])
42 self.assertEqual(x, 42)
43 state.append(x)
44 raise ZeroDivisionError()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000045 self.assertEqual(state, [1, 42, 999])
46
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000047 def test_contextmanager_no_reraise(self):
48 @contextmanager
49 def whee():
50 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000051 ctx = whee()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000052 ctx.__enter__()
53 # Calling __exit__ should not result in an exception
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000054 self.assertFalse(ctx.__exit__(TypeError, TypeError("foo"), None))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000055
56 def test_contextmanager_trap_yield_after_throw(self):
57 @contextmanager
58 def whoo():
59 try:
60 yield
61 except:
62 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000063 ctx = whoo()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000064 ctx.__enter__()
65 self.assertRaises(
66 RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
67 )
68
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000069 def test_contextmanager_except(self):
70 state = []
71 @contextmanager
72 def woohoo():
73 state.append(1)
74 try:
75 yield 42
Guido van Rossumb940e112007-01-10 16:19:56 +000076 except ZeroDivisionError as e:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000077 state.append(e.args[0])
78 self.assertEqual(state, [1, 42, 999])
79 with woohoo() as x:
80 self.assertEqual(state, [1])
81 self.assertEqual(x, 42)
82 state.append(x)
83 raise ZeroDivisionError(999)
84 self.assertEqual(state, [1, 42, 999])
85
Yury Selivanov8170e8c2015-05-09 11:44:30 -040086 def test_contextmanager_except_stopiter(self):
87 stop_exc = StopIteration('spam')
88 @contextmanager
89 def woohoo():
90 yield
91 try:
Yury Selivanov68333392015-05-22 11:16:47 -040092 with self.assertWarnsRegex(PendingDeprecationWarning,
93 "StopIteration"):
94 with woohoo():
95 raise stop_exc
Yury Selivanov8170e8c2015-05-09 11:44:30 -040096 except Exception as ex:
97 self.assertIs(ex, stop_exc)
98 else:
99 self.fail('StopIteration was suppressed')
100
101 def test_contextmanager_except_pep479(self):
102 code = """\
103from __future__ import generator_stop
104from contextlib import contextmanager
105@contextmanager
106def woohoo():
107 yield
108"""
109 locals = {}
110 exec(code, locals, locals)
111 woohoo = locals['woohoo']
112
113 stop_exc = StopIteration('spam')
114 try:
115 with woohoo():
116 raise stop_exc
117 except Exception as ex:
118 self.assertIs(ex, stop_exc)
119 else:
120 self.fail('StopIteration was suppressed')
121
R. David Murray378c0cf2010-02-24 01:46:21 +0000122 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000123 def attribs(**kw):
124 def decorate(func):
125 for k,v in kw.items():
126 setattr(func,k,v)
127 return func
128 return decorate
129 @contextmanager
130 @attribs(foo='bar')
131 def baz(spam):
132 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000133 return baz
134
135 def test_contextmanager_attribs(self):
136 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000137 self.assertEqual(baz.__name__,'baz')
138 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000139
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000140 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000141 def test_contextmanager_doc_attrib(self):
142 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000143 self.assertEqual(baz.__doc__, "Whee!")
144
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000145 @support.requires_docstrings
146 def test_instance_docstring_given_cm_docstring(self):
147 baz = self._create_contextmanager_attribs()(None)
148 self.assertEqual(baz.__doc__, "Whee!")
149
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300150 def test_keywords(self):
151 # Ensure no keyword arguments are inhibited
152 @contextmanager
153 def woohoo(self, func, args, kwds):
154 yield (self, func, args, kwds)
155 with woohoo(self=11, func=22, args=33, kwds=44) as target:
156 self.assertEqual(target, (11, 22, 33, 44))
157
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000158
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000159class ClosingTestCase(unittest.TestCase):
160
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000161 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000162 def test_instance_docs(self):
163 # Issue 19330: ensure context manager instances have good docstrings
164 cm_docstring = closing.__doc__
165 obj = closing(None)
166 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000167
168 def test_closing(self):
169 state = []
170 class C:
171 def close(self):
172 state.append(1)
173 x = C()
174 self.assertEqual(state, [])
175 with closing(x) as y:
176 self.assertEqual(x, y)
177 self.assertEqual(state, [1])
178
179 def test_closing_error(self):
180 state = []
181 class C:
182 def close(self):
183 state.append(1)
184 x = C()
185 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000186 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000187 with closing(x) as y:
188 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000189 1 / 0
190 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000191
192class FileContextTestCase(unittest.TestCase):
193
194 def testWithOpen(self):
195 tfn = tempfile.mktemp()
196 try:
197 f = None
198 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000199 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000200 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000201 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000202 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000203 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000204 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000205 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000206 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000207 1 / 0
208 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000209 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000210 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000211
Victor Stinner45df8202010-04-28 22:31:17 +0000212@unittest.skipUnless(threading, 'Threading required for this test.')
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000213class LockContextTestCase(unittest.TestCase):
214
215 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000216 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000217 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000218 self.assertTrue(locked())
219 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000220 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000221 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000222 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000223 1 / 0
224 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000225
226 def testWithLock(self):
227 lock = threading.Lock()
228 self.boilerPlate(lock, lock.locked)
229
230 def testWithRLock(self):
231 lock = threading.RLock()
232 self.boilerPlate(lock, lock._is_owned)
233
234 def testWithCondition(self):
235 lock = threading.Condition()
236 def locked():
237 return lock._is_owned()
238 self.boilerPlate(lock, locked)
239
240 def testWithSemaphore(self):
241 lock = threading.Semaphore()
242 def locked():
243 if lock.acquire(False):
244 lock.release()
245 return False
246 else:
247 return True
248 self.boilerPlate(lock, locked)
249
250 def testWithBoundedSemaphore(self):
251 lock = threading.BoundedSemaphore()
252 def locked():
253 if lock.acquire(False):
254 lock.release()
255 return False
256 else:
257 return True
258 self.boilerPlate(lock, locked)
259
Michael Foordb3a89842010-06-30 12:17:50 +0000260
261class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000262 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000263 started = False
264 exc = None
265 catch = False
266
267 def __enter__(self):
268 self.started = True
269 return self
270
271 def __exit__(self, *exc):
272 self.exc = exc
273 return self.catch
274
275
276class TestContextDecorator(unittest.TestCase):
277
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000278 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000279 def test_instance_docs(self):
280 # Issue 19330: ensure context manager instances have good docstrings
281 cm_docstring = mycontext.__doc__
282 obj = mycontext()
283 self.assertEqual(obj.__doc__, cm_docstring)
284
Michael Foordb3a89842010-06-30 12:17:50 +0000285 def test_contextdecorator(self):
286 context = mycontext()
287 with context as result:
288 self.assertIs(result, context)
289 self.assertTrue(context.started)
290
291 self.assertEqual(context.exc, (None, None, None))
292
293
294 def test_contextdecorator_with_exception(self):
295 context = mycontext()
296
Ezio Melottied3a7d22010-12-01 02:32:32 +0000297 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000298 with context:
299 raise NameError('foo')
300 self.assertIsNotNone(context.exc)
301 self.assertIs(context.exc[0], NameError)
302
303 context = mycontext()
304 context.catch = True
305 with context:
306 raise NameError('foo')
307 self.assertIsNotNone(context.exc)
308 self.assertIs(context.exc[0], NameError)
309
310
311 def test_decorator(self):
312 context = mycontext()
313
314 @context
315 def test():
316 self.assertIsNone(context.exc)
317 self.assertTrue(context.started)
318 test()
319 self.assertEqual(context.exc, (None, None, None))
320
321
322 def test_decorator_with_exception(self):
323 context = mycontext()
324
325 @context
326 def test():
327 self.assertIsNone(context.exc)
328 self.assertTrue(context.started)
329 raise NameError('foo')
330
Ezio Melottied3a7d22010-12-01 02:32:32 +0000331 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000332 test()
333 self.assertIsNotNone(context.exc)
334 self.assertIs(context.exc[0], NameError)
335
336
337 def test_decorating_method(self):
338 context = mycontext()
339
340 class Test(object):
341
342 @context
343 def method(self, a, b, c=None):
344 self.a = a
345 self.b = b
346 self.c = c
347
348 # these tests are for argument passing when used as a decorator
349 test = Test()
350 test.method(1, 2)
351 self.assertEqual(test.a, 1)
352 self.assertEqual(test.b, 2)
353 self.assertEqual(test.c, None)
354
355 test = Test()
356 test.method('a', 'b', 'c')
357 self.assertEqual(test.a, 'a')
358 self.assertEqual(test.b, 'b')
359 self.assertEqual(test.c, 'c')
360
361 test = Test()
362 test.method(a=1, b=2)
363 self.assertEqual(test.a, 1)
364 self.assertEqual(test.b, 2)
365
366
367 def test_typo_enter(self):
368 class mycontext(ContextDecorator):
369 def __unter__(self):
370 pass
371 def __exit__(self, *exc):
372 pass
373
374 with self.assertRaises(AttributeError):
375 with mycontext():
376 pass
377
378
379 def test_typo_exit(self):
380 class mycontext(ContextDecorator):
381 def __enter__(self):
382 pass
383 def __uxit__(self, *exc):
384 pass
385
386 with self.assertRaises(AttributeError):
387 with mycontext():
388 pass
389
390
391 def test_contextdecorator_as_mixin(self):
392 class somecontext(object):
393 started = False
394 exc = None
395
396 def __enter__(self):
397 self.started = True
398 return self
399
400 def __exit__(self, *exc):
401 self.exc = exc
402
403 class mycontext(somecontext, ContextDecorator):
404 pass
405
406 context = mycontext()
407 @context
408 def test():
409 self.assertIsNone(context.exc)
410 self.assertTrue(context.started)
411 test()
412 self.assertEqual(context.exc, (None, None, None))
413
414
415 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000416 @contextmanager
417 def woohoo(y):
418 state.append(y)
419 yield
420 state.append(999)
421
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000422 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000423 @woohoo(1)
424 def test(x):
425 self.assertEqual(state, [1])
426 state.append(x)
427 test('something')
428 self.assertEqual(state, [1, 'something', 999])
429
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000430 # Issue #11647: Ensure the decorated function is 'reusable'
431 state = []
432 test('something else')
433 self.assertEqual(state, [1, 'something else', 999])
434
Michael Foordb3a89842010-06-30 12:17:50 +0000435
Nick Coghlan3267a302012-05-21 22:54:43 +1000436class TestExitStack(unittest.TestCase):
437
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000438 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000439 def test_instance_docs(self):
440 # Issue 19330: ensure context manager instances have good docstrings
441 cm_docstring = ExitStack.__doc__
442 obj = ExitStack()
443 self.assertEqual(obj.__doc__, cm_docstring)
444
Nick Coghlan3267a302012-05-21 22:54:43 +1000445 def test_no_resources(self):
446 with ExitStack():
447 pass
448
449 def test_callback(self):
450 expected = [
451 ((), {}),
452 ((1,), {}),
453 ((1,2), {}),
454 ((), dict(example=1)),
455 ((1,), dict(example=1)),
456 ((1,2), dict(example=1)),
457 ]
458 result = []
459 def _exit(*args, **kwds):
460 """Test metadata propagation"""
461 result.append((args, kwds))
462 with ExitStack() as stack:
463 for args, kwds in reversed(expected):
464 if args and kwds:
465 f = stack.callback(_exit, *args, **kwds)
466 elif args:
467 f = stack.callback(_exit, *args)
468 elif kwds:
469 f = stack.callback(_exit, **kwds)
470 else:
471 f = stack.callback(_exit)
472 self.assertIs(f, _exit)
473 for wrapper in stack._exit_callbacks:
474 self.assertIs(wrapper.__wrapped__, _exit)
475 self.assertNotEqual(wrapper.__name__, _exit.__name__)
476 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
477 self.assertEqual(result, expected)
478
479 def test_push(self):
480 exc_raised = ZeroDivisionError
481 def _expect_exc(exc_type, exc, exc_tb):
482 self.assertIs(exc_type, exc_raised)
483 def _suppress_exc(*exc_details):
484 return True
485 def _expect_ok(exc_type, exc, exc_tb):
486 self.assertIsNone(exc_type)
487 self.assertIsNone(exc)
488 self.assertIsNone(exc_tb)
489 class ExitCM(object):
490 def __init__(self, check_exc):
491 self.check_exc = check_exc
492 def __enter__(self):
493 self.fail("Should not be called!")
494 def __exit__(self, *exc_details):
495 self.check_exc(*exc_details)
496 with ExitStack() as stack:
497 stack.push(_expect_ok)
498 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
499 cm = ExitCM(_expect_ok)
500 stack.push(cm)
501 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
502 stack.push(_suppress_exc)
503 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
504 cm = ExitCM(_expect_exc)
505 stack.push(cm)
506 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
507 stack.push(_expect_exc)
508 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
509 stack.push(_expect_exc)
510 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
511 1/0
512
513 def test_enter_context(self):
514 class TestCM(object):
515 def __enter__(self):
516 result.append(1)
517 def __exit__(self, *exc_details):
518 result.append(3)
519
520 result = []
521 cm = TestCM()
522 with ExitStack() as stack:
523 @stack.callback # Registered first => cleaned up last
524 def _exit():
525 result.append(4)
526 self.assertIsNotNone(_exit)
527 stack.enter_context(cm)
528 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
529 result.append(2)
530 self.assertEqual(result, [1, 2, 3, 4])
531
532 def test_close(self):
533 result = []
534 with ExitStack() as stack:
535 @stack.callback
536 def _exit():
537 result.append(1)
538 self.assertIsNotNone(_exit)
539 stack.close()
540 result.append(2)
541 self.assertEqual(result, [1, 2])
542
543 def test_pop_all(self):
544 result = []
545 with ExitStack() as stack:
546 @stack.callback
547 def _exit():
548 result.append(3)
549 self.assertIsNotNone(_exit)
550 new_stack = stack.pop_all()
551 result.append(1)
552 result.append(2)
553 new_stack.close()
554 self.assertEqual(result, [1, 2, 3])
555
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000556 def test_exit_raise(self):
557 with self.assertRaises(ZeroDivisionError):
558 with ExitStack() as stack:
559 stack.push(lambda *exc: False)
560 1/0
561
562 def test_exit_suppress(self):
563 with ExitStack() as stack:
564 stack.push(lambda *exc: True)
565 1/0
566
567 def test_exit_exception_chaining_reference(self):
568 # Sanity check to make sure that ExitStack chaining matches
569 # actual nested with statements
570 class RaiseExc:
571 def __init__(self, exc):
572 self.exc = exc
573 def __enter__(self):
574 return self
575 def __exit__(self, *exc_details):
576 raise self.exc
577
Nick Coghlan77452fc2012-06-01 22:48:32 +1000578 class RaiseExcWithContext:
579 def __init__(self, outer, inner):
580 self.outer = outer
581 self.inner = inner
582 def __enter__(self):
583 return self
584 def __exit__(self, *exc_details):
585 try:
586 raise self.inner
587 except:
588 raise self.outer
589
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000590 class SuppressExc:
591 def __enter__(self):
592 return self
593 def __exit__(self, *exc_details):
594 type(self).saved_details = exc_details
595 return True
596
597 try:
598 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000599 with RaiseExcWithContext(KeyError, AttributeError):
600 with SuppressExc():
601 with RaiseExc(ValueError):
602 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000603 except IndexError as exc:
604 self.assertIsInstance(exc.__context__, KeyError)
605 self.assertIsInstance(exc.__context__.__context__, AttributeError)
606 # Inner exceptions were suppressed
607 self.assertIsNone(exc.__context__.__context__.__context__)
608 else:
609 self.fail("Expected IndexError, but no exception was raised")
610 # Check the inner exceptions
611 inner_exc = SuppressExc.saved_details[1]
612 self.assertIsInstance(inner_exc, ValueError)
613 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
614
615 def test_exit_exception_chaining(self):
616 # Ensure exception chaining matches the reference behaviour
617 def raise_exc(exc):
618 raise exc
619
620 saved_details = None
621 def suppress_exc(*exc_details):
622 nonlocal saved_details
623 saved_details = exc_details
624 return True
625
626 try:
627 with ExitStack() as stack:
628 stack.callback(raise_exc, IndexError)
629 stack.callback(raise_exc, KeyError)
630 stack.callback(raise_exc, AttributeError)
631 stack.push(suppress_exc)
632 stack.callback(raise_exc, ValueError)
633 1 / 0
634 except IndexError as exc:
635 self.assertIsInstance(exc.__context__, KeyError)
636 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000637 # Inner exceptions were suppressed
638 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000639 else:
640 self.fail("Expected IndexError, but no exception was raised")
641 # Check the inner exceptions
642 inner_exc = saved_details[1]
643 self.assertIsInstance(inner_exc, ValueError)
644 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
645
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000646 def test_exit_exception_non_suppressing(self):
647 # http://bugs.python.org/issue19092
648 def raise_exc(exc):
649 raise exc
650
651 def suppress_exc(*exc_details):
652 return True
653
654 try:
655 with ExitStack() as stack:
656 stack.callback(lambda: None)
657 stack.callback(raise_exc, IndexError)
658 except Exception as exc:
659 self.assertIsInstance(exc, IndexError)
660 else:
661 self.fail("Expected IndexError, but no exception was raised")
662
663 try:
664 with ExitStack() as stack:
665 stack.callback(raise_exc, KeyError)
666 stack.push(suppress_exc)
667 stack.callback(raise_exc, IndexError)
668 except Exception as exc:
669 self.assertIsInstance(exc, KeyError)
670 else:
671 self.fail("Expected KeyError, but no exception was raised")
672
Nick Coghlan09761e72014-01-22 22:24:46 +1000673 def test_exit_exception_with_correct_context(self):
674 # http://bugs.python.org/issue20317
675 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000676 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000677 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000678 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000679 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000680 raise exc
681
682 exc1 = Exception(1)
683 exc2 = Exception(2)
684 exc3 = Exception(3)
685 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000686
687 # The contextmanager already fixes the context, so prior to the
688 # fix, ExitStack would try to fix it *again* and get into an
689 # infinite self-referential loop
690 try:
691 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000692 stack.enter_context(gets_the_context_right(exc4))
693 stack.enter_context(gets_the_context_right(exc3))
694 stack.enter_context(gets_the_context_right(exc2))
695 raise exc1
696 except Exception as exc:
697 self.assertIs(exc, exc4)
698 self.assertIs(exc.__context__, exc3)
699 self.assertIs(exc.__context__.__context__, exc2)
700 self.assertIs(exc.__context__.__context__.__context__, exc1)
701 self.assertIsNone(
702 exc.__context__.__context__.__context__.__context__)
703
704 def test_exit_exception_with_existing_context(self):
705 # Addresses a lack of test coverage discovered after checking in a
706 # fix for issue 20317 that still contained debugging code.
707 def raise_nested(inner_exc, outer_exc):
708 try:
709 raise inner_exc
710 finally:
711 raise outer_exc
712 exc1 = Exception(1)
713 exc2 = Exception(2)
714 exc3 = Exception(3)
715 exc4 = Exception(4)
716 exc5 = Exception(5)
717 try:
718 with ExitStack() as stack:
719 stack.callback(raise_nested, exc4, exc5)
720 stack.callback(raise_nested, exc2, exc3)
721 raise exc1
722 except Exception as exc:
723 self.assertIs(exc, exc5)
724 self.assertIs(exc.__context__, exc4)
725 self.assertIs(exc.__context__.__context__, exc3)
726 self.assertIs(exc.__context__.__context__.__context__, exc2)
727 self.assertIs(
728 exc.__context__.__context__.__context__.__context__, exc1)
729 self.assertIsNone(
730 exc.__context__.__context__.__context__.__context__.__context__)
731
Nick Coghlan09761e72014-01-22 22:24:46 +1000732
733
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000734 def test_body_exception_suppress(self):
735 def suppress_exc(*exc_details):
736 return True
737 try:
738 with ExitStack() as stack:
739 stack.push(suppress_exc)
740 1/0
741 except IndexError as exc:
742 self.fail("Expected no exception, got IndexError")
743
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000744 def test_exit_exception_chaining_suppress(self):
745 with ExitStack() as stack:
746 stack.push(lambda *exc: True)
747 stack.push(lambda *exc: 1/0)
748 stack.push(lambda *exc: {}[1])
749
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000750 def test_excessive_nesting(self):
751 # The original implementation would die with RecursionError here
752 with ExitStack() as stack:
753 for i in range(10000):
754 stack.callback(int)
755
Nick Coghlan3267a302012-05-21 22:54:43 +1000756 def test_instance_bypass(self):
757 class Example(object): pass
758 cm = Example()
759 cm.__exit__ = object()
760 stack = ExitStack()
761 self.assertRaises(AttributeError, stack.enter_context, cm)
762 stack.push(cm)
763 self.assertIs(stack._exit_callbacks[-1], cm)
764
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700765 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300766 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700767 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300768 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700769
770 @contextmanager
771 def second():
772 try:
773 yield 1
774 except Exception as exc:
775 raise UniqueException("new exception") from exc
776
777 @contextmanager
778 def first():
779 try:
780 yield 1
781 except Exception as exc:
782 raise exc
783
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300784 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700785 # handler which chain raised a new UniqueException.
786 with self.assertRaises(UniqueException) as err_ctx:
787 with ExitStack() as es_ctx:
788 es_ctx.enter_context(second())
789 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300790 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700791
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300792 exc = err_ctx.exception
793 self.assertIsInstance(exc, UniqueException)
794 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
795 self.assertIsNone(exc.__context__.__context__)
796 self.assertIsNone(exc.__context__.__cause__)
797 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700798
Berker Peksagbb44fe02014-11-28 23:28:06 +0200799
800class TestRedirectStream:
801
802 redirect_stream = None
803 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700804
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000805 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000806 def test_instance_docs(self):
807 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200808 cm_docstring = self.redirect_stream.__doc__
809 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000810 self.assertEqual(obj.__doc__, cm_docstring)
811
Nick Coghlan8e113b42013-11-03 17:00:51 +1000812 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200813 orig_stdout = getattr(sys, self.orig_stream)
814 self.redirect_stream(None)
815 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000816
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700817 def test_redirect_to_string_io(self):
818 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000819 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200820 orig_stdout = getattr(sys, self.orig_stream)
821 with self.redirect_stream(f):
822 print(msg, file=getattr(sys, self.orig_stream))
823 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000824 s = f.getvalue().strip()
825 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000826
Nick Coghlan8608d262013-10-20 00:30:51 +1000827 def test_enter_result_is_target(self):
828 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200829 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000830 self.assertIs(enter_result, f)
831
832 def test_cm_is_reusable(self):
833 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200834 write_to_f = self.redirect_stream(f)
835 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000836 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200837 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000838 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200839 print("World!", file=getattr(sys, self.orig_stream))
840 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000841 s = f.getvalue()
842 self.assertEqual(s, "Hello World!\n")
843
Nick Coghlan8e113b42013-11-03 17:00:51 +1000844 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000845 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200846 write_to_f = self.redirect_stream(f)
847 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000848 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200849 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000850 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200851 print("World!", file=getattr(sys, self.orig_stream))
852 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000853 s = f.getvalue()
854 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000855
856
Berker Peksagbb44fe02014-11-28 23:28:06 +0200857class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
858
859 redirect_stream = redirect_stdout
860 orig_stream = "stdout"
861
862
863class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
864
865 redirect_stream = redirect_stderr
866 orig_stream = "stderr"
867
868
Nick Coghlan240f86d2013-10-17 23:40:57 +1000869class TestSuppress(unittest.TestCase):
870
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
874 cm_docstring = suppress.__doc__
875 obj = suppress()
876 self.assertEqual(obj.__doc__, cm_docstring)
877
Nick Coghlan8608d262013-10-20 00:30:51 +1000878 def test_no_result_from_enter(self):
879 with suppress(ValueError) as enter_result:
880 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000881
Nick Coghlan8608d262013-10-20 00:30:51 +1000882 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000883 with suppress(ValueError):
884 self.assertEqual(pow(2, 5), 32)
885
886 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000887 with suppress(TypeError):
888 len(5)
889
Nick Coghlan059def52013-10-26 18:08:15 +1000890 def test_exception_hierarchy(self):
891 with suppress(LookupError):
892 'Hello'[50]
893
894 def test_other_exception(self):
895 with self.assertRaises(ZeroDivisionError):
896 with suppress(TypeError):
897 1/0
898
899 def test_no_args(self):
900 with self.assertRaises(ZeroDivisionError):
901 with suppress():
902 1/0
903
Nick Coghlan240f86d2013-10-17 23:40:57 +1000904 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000905 with suppress(ZeroDivisionError, TypeError):
906 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000907 with suppress(ZeroDivisionError, TypeError):
908 len(5)
909
Nick Coghlan8608d262013-10-20 00:30:51 +1000910 def test_cm_is_reentrant(self):
911 ignore_exceptions = suppress(Exception)
912 with ignore_exceptions:
913 pass
914 with ignore_exceptions:
915 len(5)
916 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +1000917 with ignore_exceptions: # Check nested usage
918 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +0000919 outer_continued = True
920 1/0
921 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +1000922
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000923if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400924 unittest.main()