blob: c04c804af5704703872a4490700be52b443aa12f [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
R. David Murray378c0cf2010-02-24 01:46:21 +0000155 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000156 def attribs(**kw):
157 def decorate(func):
158 for k,v in kw.items():
159 setattr(func,k,v)
160 return func
161 return decorate
162 @contextmanager
163 @attribs(foo='bar')
164 def baz(spam):
165 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000166 return baz
167
168 def test_contextmanager_attribs(self):
169 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000170 self.assertEqual(baz.__name__,'baz')
171 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000172
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000173 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000174 def test_contextmanager_doc_attrib(self):
175 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000176 self.assertEqual(baz.__doc__, "Whee!")
177
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000178 @support.requires_docstrings
179 def test_instance_docstring_given_cm_docstring(self):
180 baz = self._create_contextmanager_attribs()(None)
181 self.assertEqual(baz.__doc__, "Whee!")
182
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300183 def test_keywords(self):
184 # Ensure no keyword arguments are inhibited
185 @contextmanager
186 def woohoo(self, func, args, kwds):
187 yield (self, func, args, kwds)
188 with woohoo(self=11, func=22, args=33, kwds=44) as target:
189 self.assertEqual(target, (11, 22, 33, 44))
190
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000191
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000192class ClosingTestCase(unittest.TestCase):
193
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000194 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000195 def test_instance_docs(self):
196 # Issue 19330: ensure context manager instances have good docstrings
197 cm_docstring = closing.__doc__
198 obj = closing(None)
199 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000200
201 def test_closing(self):
202 state = []
203 class C:
204 def close(self):
205 state.append(1)
206 x = C()
207 self.assertEqual(state, [])
208 with closing(x) as y:
209 self.assertEqual(x, y)
210 self.assertEqual(state, [1])
211
212 def test_closing_error(self):
213 state = []
214 class C:
215 def close(self):
216 state.append(1)
217 x = C()
218 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000219 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000220 with closing(x) as y:
221 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000222 1 / 0
223 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000224
225class FileContextTestCase(unittest.TestCase):
226
227 def testWithOpen(self):
228 tfn = tempfile.mktemp()
229 try:
230 f = None
231 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000232 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000233 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000234 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000235 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000236 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000237 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000238 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000239 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000240 1 / 0
241 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000242 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000243 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000244
Victor Stinner45df8202010-04-28 22:31:17 +0000245@unittest.skipUnless(threading, 'Threading required for this test.')
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000246class LockContextTestCase(unittest.TestCase):
247
248 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000249 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000250 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000251 self.assertTrue(locked())
252 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000253 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000254 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000255 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000256 1 / 0
257 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000258
259 def testWithLock(self):
260 lock = threading.Lock()
261 self.boilerPlate(lock, lock.locked)
262
263 def testWithRLock(self):
264 lock = threading.RLock()
265 self.boilerPlate(lock, lock._is_owned)
266
267 def testWithCondition(self):
268 lock = threading.Condition()
269 def locked():
270 return lock._is_owned()
271 self.boilerPlate(lock, locked)
272
273 def testWithSemaphore(self):
274 lock = threading.Semaphore()
275 def locked():
276 if lock.acquire(False):
277 lock.release()
278 return False
279 else:
280 return True
281 self.boilerPlate(lock, locked)
282
283 def testWithBoundedSemaphore(self):
284 lock = threading.BoundedSemaphore()
285 def locked():
286 if lock.acquire(False):
287 lock.release()
288 return False
289 else:
290 return True
291 self.boilerPlate(lock, locked)
292
Michael Foordb3a89842010-06-30 12:17:50 +0000293
294class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000295 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000296 started = False
297 exc = None
298 catch = False
299
300 def __enter__(self):
301 self.started = True
302 return self
303
304 def __exit__(self, *exc):
305 self.exc = exc
306 return self.catch
307
308
309class TestContextDecorator(unittest.TestCase):
310
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000311 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000312 def test_instance_docs(self):
313 # Issue 19330: ensure context manager instances have good docstrings
314 cm_docstring = mycontext.__doc__
315 obj = mycontext()
316 self.assertEqual(obj.__doc__, cm_docstring)
317
Michael Foordb3a89842010-06-30 12:17:50 +0000318 def test_contextdecorator(self):
319 context = mycontext()
320 with context as result:
321 self.assertIs(result, context)
322 self.assertTrue(context.started)
323
324 self.assertEqual(context.exc, (None, None, None))
325
326
327 def test_contextdecorator_with_exception(self):
328 context = mycontext()
329
Ezio Melottied3a7d22010-12-01 02:32:32 +0000330 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000331 with context:
332 raise NameError('foo')
333 self.assertIsNotNone(context.exc)
334 self.assertIs(context.exc[0], NameError)
335
336 context = mycontext()
337 context.catch = True
338 with context:
339 raise NameError('foo')
340 self.assertIsNotNone(context.exc)
341 self.assertIs(context.exc[0], NameError)
342
343
344 def test_decorator(self):
345 context = mycontext()
346
347 @context
348 def test():
349 self.assertIsNone(context.exc)
350 self.assertTrue(context.started)
351 test()
352 self.assertEqual(context.exc, (None, None, None))
353
354
355 def test_decorator_with_exception(self):
356 context = mycontext()
357
358 @context
359 def test():
360 self.assertIsNone(context.exc)
361 self.assertTrue(context.started)
362 raise NameError('foo')
363
Ezio Melottied3a7d22010-12-01 02:32:32 +0000364 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000365 test()
366 self.assertIsNotNone(context.exc)
367 self.assertIs(context.exc[0], NameError)
368
369
370 def test_decorating_method(self):
371 context = mycontext()
372
373 class Test(object):
374
375 @context
376 def method(self, a, b, c=None):
377 self.a = a
378 self.b = b
379 self.c = c
380
381 # these tests are for argument passing when used as a decorator
382 test = Test()
383 test.method(1, 2)
384 self.assertEqual(test.a, 1)
385 self.assertEqual(test.b, 2)
386 self.assertEqual(test.c, None)
387
388 test = Test()
389 test.method('a', 'b', 'c')
390 self.assertEqual(test.a, 'a')
391 self.assertEqual(test.b, 'b')
392 self.assertEqual(test.c, 'c')
393
394 test = Test()
395 test.method(a=1, b=2)
396 self.assertEqual(test.a, 1)
397 self.assertEqual(test.b, 2)
398
399
400 def test_typo_enter(self):
401 class mycontext(ContextDecorator):
402 def __unter__(self):
403 pass
404 def __exit__(self, *exc):
405 pass
406
407 with self.assertRaises(AttributeError):
408 with mycontext():
409 pass
410
411
412 def test_typo_exit(self):
413 class mycontext(ContextDecorator):
414 def __enter__(self):
415 pass
416 def __uxit__(self, *exc):
417 pass
418
419 with self.assertRaises(AttributeError):
420 with mycontext():
421 pass
422
423
424 def test_contextdecorator_as_mixin(self):
425 class somecontext(object):
426 started = False
427 exc = None
428
429 def __enter__(self):
430 self.started = True
431 return self
432
433 def __exit__(self, *exc):
434 self.exc = exc
435
436 class mycontext(somecontext, ContextDecorator):
437 pass
438
439 context = mycontext()
440 @context
441 def test():
442 self.assertIsNone(context.exc)
443 self.assertTrue(context.started)
444 test()
445 self.assertEqual(context.exc, (None, None, None))
446
447
448 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000449 @contextmanager
450 def woohoo(y):
451 state.append(y)
452 yield
453 state.append(999)
454
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000455 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000456 @woohoo(1)
457 def test(x):
458 self.assertEqual(state, [1])
459 state.append(x)
460 test('something')
461 self.assertEqual(state, [1, 'something', 999])
462
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000463 # Issue #11647: Ensure the decorated function is 'reusable'
464 state = []
465 test('something else')
466 self.assertEqual(state, [1, 'something else', 999])
467
Michael Foordb3a89842010-06-30 12:17:50 +0000468
Nick Coghlan3267a302012-05-21 22:54:43 +1000469class TestExitStack(unittest.TestCase):
470
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000471 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000472 def test_instance_docs(self):
473 # Issue 19330: ensure context manager instances have good docstrings
474 cm_docstring = ExitStack.__doc__
475 obj = ExitStack()
476 self.assertEqual(obj.__doc__, cm_docstring)
477
Nick Coghlan3267a302012-05-21 22:54:43 +1000478 def test_no_resources(self):
479 with ExitStack():
480 pass
481
482 def test_callback(self):
483 expected = [
484 ((), {}),
485 ((1,), {}),
486 ((1,2), {}),
487 ((), dict(example=1)),
488 ((1,), dict(example=1)),
489 ((1,2), dict(example=1)),
490 ]
491 result = []
492 def _exit(*args, **kwds):
493 """Test metadata propagation"""
494 result.append((args, kwds))
495 with ExitStack() as stack:
496 for args, kwds in reversed(expected):
497 if args and kwds:
498 f = stack.callback(_exit, *args, **kwds)
499 elif args:
500 f = stack.callback(_exit, *args)
501 elif kwds:
502 f = stack.callback(_exit, **kwds)
503 else:
504 f = stack.callback(_exit)
505 self.assertIs(f, _exit)
506 for wrapper in stack._exit_callbacks:
507 self.assertIs(wrapper.__wrapped__, _exit)
508 self.assertNotEqual(wrapper.__name__, _exit.__name__)
509 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
510 self.assertEqual(result, expected)
511
512 def test_push(self):
513 exc_raised = ZeroDivisionError
514 def _expect_exc(exc_type, exc, exc_tb):
515 self.assertIs(exc_type, exc_raised)
516 def _suppress_exc(*exc_details):
517 return True
518 def _expect_ok(exc_type, exc, exc_tb):
519 self.assertIsNone(exc_type)
520 self.assertIsNone(exc)
521 self.assertIsNone(exc_tb)
522 class ExitCM(object):
523 def __init__(self, check_exc):
524 self.check_exc = check_exc
525 def __enter__(self):
526 self.fail("Should not be called!")
527 def __exit__(self, *exc_details):
528 self.check_exc(*exc_details)
529 with ExitStack() as stack:
530 stack.push(_expect_ok)
531 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
532 cm = ExitCM(_expect_ok)
533 stack.push(cm)
534 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
535 stack.push(_suppress_exc)
536 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
537 cm = ExitCM(_expect_exc)
538 stack.push(cm)
539 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
540 stack.push(_expect_exc)
541 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
542 stack.push(_expect_exc)
543 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
544 1/0
545
546 def test_enter_context(self):
547 class TestCM(object):
548 def __enter__(self):
549 result.append(1)
550 def __exit__(self, *exc_details):
551 result.append(3)
552
553 result = []
554 cm = TestCM()
555 with ExitStack() as stack:
556 @stack.callback # Registered first => cleaned up last
557 def _exit():
558 result.append(4)
559 self.assertIsNotNone(_exit)
560 stack.enter_context(cm)
561 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
562 result.append(2)
563 self.assertEqual(result, [1, 2, 3, 4])
564
565 def test_close(self):
566 result = []
567 with ExitStack() as stack:
568 @stack.callback
569 def _exit():
570 result.append(1)
571 self.assertIsNotNone(_exit)
572 stack.close()
573 result.append(2)
574 self.assertEqual(result, [1, 2])
575
576 def test_pop_all(self):
577 result = []
578 with ExitStack() as stack:
579 @stack.callback
580 def _exit():
581 result.append(3)
582 self.assertIsNotNone(_exit)
583 new_stack = stack.pop_all()
584 result.append(1)
585 result.append(2)
586 new_stack.close()
587 self.assertEqual(result, [1, 2, 3])
588
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000589 def test_exit_raise(self):
590 with self.assertRaises(ZeroDivisionError):
591 with ExitStack() as stack:
592 stack.push(lambda *exc: False)
593 1/0
594
595 def test_exit_suppress(self):
596 with ExitStack() as stack:
597 stack.push(lambda *exc: True)
598 1/0
599
600 def test_exit_exception_chaining_reference(self):
601 # Sanity check to make sure that ExitStack chaining matches
602 # actual nested with statements
603 class RaiseExc:
604 def __init__(self, exc):
605 self.exc = exc
606 def __enter__(self):
607 return self
608 def __exit__(self, *exc_details):
609 raise self.exc
610
Nick Coghlan77452fc2012-06-01 22:48:32 +1000611 class RaiseExcWithContext:
612 def __init__(self, outer, inner):
613 self.outer = outer
614 self.inner = inner
615 def __enter__(self):
616 return self
617 def __exit__(self, *exc_details):
618 try:
619 raise self.inner
620 except:
621 raise self.outer
622
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000623 class SuppressExc:
624 def __enter__(self):
625 return self
626 def __exit__(self, *exc_details):
627 type(self).saved_details = exc_details
628 return True
629
630 try:
631 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000632 with RaiseExcWithContext(KeyError, AttributeError):
633 with SuppressExc():
634 with RaiseExc(ValueError):
635 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000636 except IndexError as exc:
637 self.assertIsInstance(exc.__context__, KeyError)
638 self.assertIsInstance(exc.__context__.__context__, AttributeError)
639 # Inner exceptions were suppressed
640 self.assertIsNone(exc.__context__.__context__.__context__)
641 else:
642 self.fail("Expected IndexError, but no exception was raised")
643 # Check the inner exceptions
644 inner_exc = SuppressExc.saved_details[1]
645 self.assertIsInstance(inner_exc, ValueError)
646 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
647
648 def test_exit_exception_chaining(self):
649 # Ensure exception chaining matches the reference behaviour
650 def raise_exc(exc):
651 raise exc
652
653 saved_details = None
654 def suppress_exc(*exc_details):
655 nonlocal saved_details
656 saved_details = exc_details
657 return True
658
659 try:
660 with ExitStack() as stack:
661 stack.callback(raise_exc, IndexError)
662 stack.callback(raise_exc, KeyError)
663 stack.callback(raise_exc, AttributeError)
664 stack.push(suppress_exc)
665 stack.callback(raise_exc, ValueError)
666 1 / 0
667 except IndexError as exc:
668 self.assertIsInstance(exc.__context__, KeyError)
669 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000670 # Inner exceptions were suppressed
671 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000672 else:
673 self.fail("Expected IndexError, but no exception was raised")
674 # Check the inner exceptions
675 inner_exc = saved_details[1]
676 self.assertIsInstance(inner_exc, ValueError)
677 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
678
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000679 def test_exit_exception_non_suppressing(self):
680 # http://bugs.python.org/issue19092
681 def raise_exc(exc):
682 raise exc
683
684 def suppress_exc(*exc_details):
685 return True
686
687 try:
688 with ExitStack() as stack:
689 stack.callback(lambda: None)
690 stack.callback(raise_exc, IndexError)
691 except Exception as exc:
692 self.assertIsInstance(exc, IndexError)
693 else:
694 self.fail("Expected IndexError, but no exception was raised")
695
696 try:
697 with ExitStack() as stack:
698 stack.callback(raise_exc, KeyError)
699 stack.push(suppress_exc)
700 stack.callback(raise_exc, IndexError)
701 except Exception as exc:
702 self.assertIsInstance(exc, KeyError)
703 else:
704 self.fail("Expected KeyError, but no exception was raised")
705
Nick Coghlan09761e72014-01-22 22:24:46 +1000706 def test_exit_exception_with_correct_context(self):
707 # http://bugs.python.org/issue20317
708 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000709 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000710 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000711 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000712 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000713 raise exc
714
715 exc1 = Exception(1)
716 exc2 = Exception(2)
717 exc3 = Exception(3)
718 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000719
720 # The contextmanager already fixes the context, so prior to the
721 # fix, ExitStack would try to fix it *again* and get into an
722 # infinite self-referential loop
723 try:
724 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000725 stack.enter_context(gets_the_context_right(exc4))
726 stack.enter_context(gets_the_context_right(exc3))
727 stack.enter_context(gets_the_context_right(exc2))
728 raise exc1
729 except Exception as exc:
730 self.assertIs(exc, exc4)
731 self.assertIs(exc.__context__, exc3)
732 self.assertIs(exc.__context__.__context__, exc2)
733 self.assertIs(exc.__context__.__context__.__context__, exc1)
734 self.assertIsNone(
735 exc.__context__.__context__.__context__.__context__)
736
737 def test_exit_exception_with_existing_context(self):
738 # Addresses a lack of test coverage discovered after checking in a
739 # fix for issue 20317 that still contained debugging code.
740 def raise_nested(inner_exc, outer_exc):
741 try:
742 raise inner_exc
743 finally:
744 raise outer_exc
745 exc1 = Exception(1)
746 exc2 = Exception(2)
747 exc3 = Exception(3)
748 exc4 = Exception(4)
749 exc5 = Exception(5)
750 try:
751 with ExitStack() as stack:
752 stack.callback(raise_nested, exc4, exc5)
753 stack.callback(raise_nested, exc2, exc3)
754 raise exc1
755 except Exception as exc:
756 self.assertIs(exc, exc5)
757 self.assertIs(exc.__context__, exc4)
758 self.assertIs(exc.__context__.__context__, exc3)
759 self.assertIs(exc.__context__.__context__.__context__, exc2)
760 self.assertIs(
761 exc.__context__.__context__.__context__.__context__, exc1)
762 self.assertIsNone(
763 exc.__context__.__context__.__context__.__context__.__context__)
764
Nick Coghlan09761e72014-01-22 22:24:46 +1000765
766
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000767 def test_body_exception_suppress(self):
768 def suppress_exc(*exc_details):
769 return True
770 try:
771 with ExitStack() as stack:
772 stack.push(suppress_exc)
773 1/0
774 except IndexError as exc:
775 self.fail("Expected no exception, got IndexError")
776
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000777 def test_exit_exception_chaining_suppress(self):
778 with ExitStack() as stack:
779 stack.push(lambda *exc: True)
780 stack.push(lambda *exc: 1/0)
781 stack.push(lambda *exc: {}[1])
782
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000783 def test_excessive_nesting(self):
784 # The original implementation would die with RecursionError here
785 with ExitStack() as stack:
786 for i in range(10000):
787 stack.callback(int)
788
Nick Coghlan3267a302012-05-21 22:54:43 +1000789 def test_instance_bypass(self):
790 class Example(object): pass
791 cm = Example()
792 cm.__exit__ = object()
793 stack = ExitStack()
794 self.assertRaises(AttributeError, stack.enter_context, cm)
795 stack.push(cm)
796 self.assertIs(stack._exit_callbacks[-1], cm)
797
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700798 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300799 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700800 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300801 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700802
803 @contextmanager
804 def second():
805 try:
806 yield 1
807 except Exception as exc:
808 raise UniqueException("new exception") from exc
809
810 @contextmanager
811 def first():
812 try:
813 yield 1
814 except Exception as exc:
815 raise exc
816
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300817 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700818 # handler which chain raised a new UniqueException.
819 with self.assertRaises(UniqueException) as err_ctx:
820 with ExitStack() as es_ctx:
821 es_ctx.enter_context(second())
822 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300823 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700824
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300825 exc = err_ctx.exception
826 self.assertIsInstance(exc, UniqueException)
827 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
828 self.assertIsNone(exc.__context__.__context__)
829 self.assertIsNone(exc.__context__.__cause__)
830 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700831
Berker Peksagbb44fe02014-11-28 23:28:06 +0200832
833class TestRedirectStream:
834
835 redirect_stream = None
836 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700837
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000838 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000839 def test_instance_docs(self):
840 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200841 cm_docstring = self.redirect_stream.__doc__
842 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000843 self.assertEqual(obj.__doc__, cm_docstring)
844
Nick Coghlan8e113b42013-11-03 17:00:51 +1000845 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200846 orig_stdout = getattr(sys, self.orig_stream)
847 self.redirect_stream(None)
848 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000849
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700850 def test_redirect_to_string_io(self):
851 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000852 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200853 orig_stdout = getattr(sys, self.orig_stream)
854 with self.redirect_stream(f):
855 print(msg, file=getattr(sys, self.orig_stream))
856 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000857 s = f.getvalue().strip()
858 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000859
Nick Coghlan8608d262013-10-20 00:30:51 +1000860 def test_enter_result_is_target(self):
861 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200862 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000863 self.assertIs(enter_result, f)
864
865 def test_cm_is_reusable(self):
866 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200867 write_to_f = self.redirect_stream(f)
868 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000869 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200870 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000871 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200872 print("World!", file=getattr(sys, self.orig_stream))
873 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000874 s = f.getvalue()
875 self.assertEqual(s, "Hello World!\n")
876
Nick Coghlan8e113b42013-11-03 17:00:51 +1000877 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000878 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200879 write_to_f = self.redirect_stream(f)
880 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000881 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200882 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000883 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200884 print("World!", file=getattr(sys, self.orig_stream))
885 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000886 s = f.getvalue()
887 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000888
889
Berker Peksagbb44fe02014-11-28 23:28:06 +0200890class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
891
892 redirect_stream = redirect_stdout
893 orig_stream = "stdout"
894
895
896class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
897
898 redirect_stream = redirect_stderr
899 orig_stream = "stderr"
900
901
Nick Coghlan240f86d2013-10-17 23:40:57 +1000902class TestSuppress(unittest.TestCase):
903
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000904 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000905 def test_instance_docs(self):
906 # Issue 19330: ensure context manager instances have good docstrings
907 cm_docstring = suppress.__doc__
908 obj = suppress()
909 self.assertEqual(obj.__doc__, cm_docstring)
910
Nick Coghlan8608d262013-10-20 00:30:51 +1000911 def test_no_result_from_enter(self):
912 with suppress(ValueError) as enter_result:
913 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000914
Nick Coghlan8608d262013-10-20 00:30:51 +1000915 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000916 with suppress(ValueError):
917 self.assertEqual(pow(2, 5), 32)
918
919 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000920 with suppress(TypeError):
921 len(5)
922
Nick Coghlan059def52013-10-26 18:08:15 +1000923 def test_exception_hierarchy(self):
924 with suppress(LookupError):
925 'Hello'[50]
926
927 def test_other_exception(self):
928 with self.assertRaises(ZeroDivisionError):
929 with suppress(TypeError):
930 1/0
931
932 def test_no_args(self):
933 with self.assertRaises(ZeroDivisionError):
934 with suppress():
935 1/0
936
Nick Coghlan240f86d2013-10-17 23:40:57 +1000937 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000938 with suppress(ZeroDivisionError, TypeError):
939 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000940 with suppress(ZeroDivisionError, TypeError):
941 len(5)
942
Nick Coghlan8608d262013-10-20 00:30:51 +1000943 def test_cm_is_reentrant(self):
944 ignore_exceptions = suppress(Exception)
945 with ignore_exceptions:
946 pass
947 with ignore_exceptions:
948 len(5)
949 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +1000950 with ignore_exceptions: # Check nested usage
951 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +0000952 outer_continued = True
953 1/0
954 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +1000955
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000956if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400957 unittest.main()