blob: 2669651fba205fdd98c7d7d83b64b93447976f40 [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
R. David Murray378c0cf2010-02-24 01:46:21 +000086 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000087 def attribs(**kw):
88 def decorate(func):
89 for k,v in kw.items():
90 setattr(func,k,v)
91 return func
92 return decorate
93 @contextmanager
94 @attribs(foo='bar')
95 def baz(spam):
96 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +000097 return baz
98
99 def test_contextmanager_attribs(self):
100 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000101 self.assertEqual(baz.__name__,'baz')
102 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000103
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000104 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000105 def test_contextmanager_doc_attrib(self):
106 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000107 self.assertEqual(baz.__doc__, "Whee!")
108
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000109 @support.requires_docstrings
110 def test_instance_docstring_given_cm_docstring(self):
111 baz = self._create_contextmanager_attribs()(None)
112 self.assertEqual(baz.__doc__, "Whee!")
113
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300114 def test_keywords(self):
115 # Ensure no keyword arguments are inhibited
116 @contextmanager
117 def woohoo(self, func, args, kwds):
118 yield (self, func, args, kwds)
119 with woohoo(self=11, func=22, args=33, kwds=44) as target:
120 self.assertEqual(target, (11, 22, 33, 44))
121
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000122
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000123class ClosingTestCase(unittest.TestCase):
124
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000125 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000126 def test_instance_docs(self):
127 # Issue 19330: ensure context manager instances have good docstrings
128 cm_docstring = closing.__doc__
129 obj = closing(None)
130 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000131
132 def test_closing(self):
133 state = []
134 class C:
135 def close(self):
136 state.append(1)
137 x = C()
138 self.assertEqual(state, [])
139 with closing(x) as y:
140 self.assertEqual(x, y)
141 self.assertEqual(state, [1])
142
143 def test_closing_error(self):
144 state = []
145 class C:
146 def close(self):
147 state.append(1)
148 x = C()
149 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000150 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000151 with closing(x) as y:
152 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000153 1 / 0
154 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000155
156class FileContextTestCase(unittest.TestCase):
157
158 def testWithOpen(self):
159 tfn = tempfile.mktemp()
160 try:
161 f = None
162 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000163 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000164 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000165 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000166 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000167 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000168 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000169 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000170 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000171 1 / 0
172 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000173 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000174 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000175
Victor Stinner45df8202010-04-28 22:31:17 +0000176@unittest.skipUnless(threading, 'Threading required for this test.')
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000177class LockContextTestCase(unittest.TestCase):
178
179 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000180 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000181 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000182 self.assertTrue(locked())
183 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000184 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000185 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000186 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000187 1 / 0
188 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000189
190 def testWithLock(self):
191 lock = threading.Lock()
192 self.boilerPlate(lock, lock.locked)
193
194 def testWithRLock(self):
195 lock = threading.RLock()
196 self.boilerPlate(lock, lock._is_owned)
197
198 def testWithCondition(self):
199 lock = threading.Condition()
200 def locked():
201 return lock._is_owned()
202 self.boilerPlate(lock, locked)
203
204 def testWithSemaphore(self):
205 lock = threading.Semaphore()
206 def locked():
207 if lock.acquire(False):
208 lock.release()
209 return False
210 else:
211 return True
212 self.boilerPlate(lock, locked)
213
214 def testWithBoundedSemaphore(self):
215 lock = threading.BoundedSemaphore()
216 def locked():
217 if lock.acquire(False):
218 lock.release()
219 return False
220 else:
221 return True
222 self.boilerPlate(lock, locked)
223
Michael Foordb3a89842010-06-30 12:17:50 +0000224
225class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000226 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000227 started = False
228 exc = None
229 catch = False
230
231 def __enter__(self):
232 self.started = True
233 return self
234
235 def __exit__(self, *exc):
236 self.exc = exc
237 return self.catch
238
239
240class TestContextDecorator(unittest.TestCase):
241
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000242 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000243 def test_instance_docs(self):
244 # Issue 19330: ensure context manager instances have good docstrings
245 cm_docstring = mycontext.__doc__
246 obj = mycontext()
247 self.assertEqual(obj.__doc__, cm_docstring)
248
Michael Foordb3a89842010-06-30 12:17:50 +0000249 def test_contextdecorator(self):
250 context = mycontext()
251 with context as result:
252 self.assertIs(result, context)
253 self.assertTrue(context.started)
254
255 self.assertEqual(context.exc, (None, None, None))
256
257
258 def test_contextdecorator_with_exception(self):
259 context = mycontext()
260
Ezio Melottied3a7d22010-12-01 02:32:32 +0000261 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000262 with context:
263 raise NameError('foo')
264 self.assertIsNotNone(context.exc)
265 self.assertIs(context.exc[0], NameError)
266
267 context = mycontext()
268 context.catch = True
269 with context:
270 raise NameError('foo')
271 self.assertIsNotNone(context.exc)
272 self.assertIs(context.exc[0], NameError)
273
274
275 def test_decorator(self):
276 context = mycontext()
277
278 @context
279 def test():
280 self.assertIsNone(context.exc)
281 self.assertTrue(context.started)
282 test()
283 self.assertEqual(context.exc, (None, None, None))
284
285
286 def test_decorator_with_exception(self):
287 context = mycontext()
288
289 @context
290 def test():
291 self.assertIsNone(context.exc)
292 self.assertTrue(context.started)
293 raise NameError('foo')
294
Ezio Melottied3a7d22010-12-01 02:32:32 +0000295 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000296 test()
297 self.assertIsNotNone(context.exc)
298 self.assertIs(context.exc[0], NameError)
299
300
301 def test_decorating_method(self):
302 context = mycontext()
303
304 class Test(object):
305
306 @context
307 def method(self, a, b, c=None):
308 self.a = a
309 self.b = b
310 self.c = c
311
312 # these tests are for argument passing when used as a decorator
313 test = Test()
314 test.method(1, 2)
315 self.assertEqual(test.a, 1)
316 self.assertEqual(test.b, 2)
317 self.assertEqual(test.c, None)
318
319 test = Test()
320 test.method('a', 'b', 'c')
321 self.assertEqual(test.a, 'a')
322 self.assertEqual(test.b, 'b')
323 self.assertEqual(test.c, 'c')
324
325 test = Test()
326 test.method(a=1, b=2)
327 self.assertEqual(test.a, 1)
328 self.assertEqual(test.b, 2)
329
330
331 def test_typo_enter(self):
332 class mycontext(ContextDecorator):
333 def __unter__(self):
334 pass
335 def __exit__(self, *exc):
336 pass
337
338 with self.assertRaises(AttributeError):
339 with mycontext():
340 pass
341
342
343 def test_typo_exit(self):
344 class mycontext(ContextDecorator):
345 def __enter__(self):
346 pass
347 def __uxit__(self, *exc):
348 pass
349
350 with self.assertRaises(AttributeError):
351 with mycontext():
352 pass
353
354
355 def test_contextdecorator_as_mixin(self):
356 class somecontext(object):
357 started = False
358 exc = None
359
360 def __enter__(self):
361 self.started = True
362 return self
363
364 def __exit__(self, *exc):
365 self.exc = exc
366
367 class mycontext(somecontext, ContextDecorator):
368 pass
369
370 context = mycontext()
371 @context
372 def test():
373 self.assertIsNone(context.exc)
374 self.assertTrue(context.started)
375 test()
376 self.assertEqual(context.exc, (None, None, None))
377
378
379 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000380 @contextmanager
381 def woohoo(y):
382 state.append(y)
383 yield
384 state.append(999)
385
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000386 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000387 @woohoo(1)
388 def test(x):
389 self.assertEqual(state, [1])
390 state.append(x)
391 test('something')
392 self.assertEqual(state, [1, 'something', 999])
393
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000394 # Issue #11647: Ensure the decorated function is 'reusable'
395 state = []
396 test('something else')
397 self.assertEqual(state, [1, 'something else', 999])
398
Michael Foordb3a89842010-06-30 12:17:50 +0000399
Nick Coghlan3267a302012-05-21 22:54:43 +1000400class TestExitStack(unittest.TestCase):
401
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000402 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000403 def test_instance_docs(self):
404 # Issue 19330: ensure context manager instances have good docstrings
405 cm_docstring = ExitStack.__doc__
406 obj = ExitStack()
407 self.assertEqual(obj.__doc__, cm_docstring)
408
Nick Coghlan3267a302012-05-21 22:54:43 +1000409 def test_no_resources(self):
410 with ExitStack():
411 pass
412
413 def test_callback(self):
414 expected = [
415 ((), {}),
416 ((1,), {}),
417 ((1,2), {}),
418 ((), dict(example=1)),
419 ((1,), dict(example=1)),
420 ((1,2), dict(example=1)),
421 ]
422 result = []
423 def _exit(*args, **kwds):
424 """Test metadata propagation"""
425 result.append((args, kwds))
426 with ExitStack() as stack:
427 for args, kwds in reversed(expected):
428 if args and kwds:
429 f = stack.callback(_exit, *args, **kwds)
430 elif args:
431 f = stack.callback(_exit, *args)
432 elif kwds:
433 f = stack.callback(_exit, **kwds)
434 else:
435 f = stack.callback(_exit)
436 self.assertIs(f, _exit)
437 for wrapper in stack._exit_callbacks:
438 self.assertIs(wrapper.__wrapped__, _exit)
439 self.assertNotEqual(wrapper.__name__, _exit.__name__)
440 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
441 self.assertEqual(result, expected)
442
443 def test_push(self):
444 exc_raised = ZeroDivisionError
445 def _expect_exc(exc_type, exc, exc_tb):
446 self.assertIs(exc_type, exc_raised)
447 def _suppress_exc(*exc_details):
448 return True
449 def _expect_ok(exc_type, exc, exc_tb):
450 self.assertIsNone(exc_type)
451 self.assertIsNone(exc)
452 self.assertIsNone(exc_tb)
453 class ExitCM(object):
454 def __init__(self, check_exc):
455 self.check_exc = check_exc
456 def __enter__(self):
457 self.fail("Should not be called!")
458 def __exit__(self, *exc_details):
459 self.check_exc(*exc_details)
460 with ExitStack() as stack:
461 stack.push(_expect_ok)
462 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
463 cm = ExitCM(_expect_ok)
464 stack.push(cm)
465 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
466 stack.push(_suppress_exc)
467 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
468 cm = ExitCM(_expect_exc)
469 stack.push(cm)
470 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
471 stack.push(_expect_exc)
472 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
473 stack.push(_expect_exc)
474 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
475 1/0
476
477 def test_enter_context(self):
478 class TestCM(object):
479 def __enter__(self):
480 result.append(1)
481 def __exit__(self, *exc_details):
482 result.append(3)
483
484 result = []
485 cm = TestCM()
486 with ExitStack() as stack:
487 @stack.callback # Registered first => cleaned up last
488 def _exit():
489 result.append(4)
490 self.assertIsNotNone(_exit)
491 stack.enter_context(cm)
492 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
493 result.append(2)
494 self.assertEqual(result, [1, 2, 3, 4])
495
496 def test_close(self):
497 result = []
498 with ExitStack() as stack:
499 @stack.callback
500 def _exit():
501 result.append(1)
502 self.assertIsNotNone(_exit)
503 stack.close()
504 result.append(2)
505 self.assertEqual(result, [1, 2])
506
507 def test_pop_all(self):
508 result = []
509 with ExitStack() as stack:
510 @stack.callback
511 def _exit():
512 result.append(3)
513 self.assertIsNotNone(_exit)
514 new_stack = stack.pop_all()
515 result.append(1)
516 result.append(2)
517 new_stack.close()
518 self.assertEqual(result, [1, 2, 3])
519
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000520 def test_exit_raise(self):
521 with self.assertRaises(ZeroDivisionError):
522 with ExitStack() as stack:
523 stack.push(lambda *exc: False)
524 1/0
525
526 def test_exit_suppress(self):
527 with ExitStack() as stack:
528 stack.push(lambda *exc: True)
529 1/0
530
531 def test_exit_exception_chaining_reference(self):
532 # Sanity check to make sure that ExitStack chaining matches
533 # actual nested with statements
534 class RaiseExc:
535 def __init__(self, exc):
536 self.exc = exc
537 def __enter__(self):
538 return self
539 def __exit__(self, *exc_details):
540 raise self.exc
541
Nick Coghlan77452fc2012-06-01 22:48:32 +1000542 class RaiseExcWithContext:
543 def __init__(self, outer, inner):
544 self.outer = outer
545 self.inner = inner
546 def __enter__(self):
547 return self
548 def __exit__(self, *exc_details):
549 try:
550 raise self.inner
551 except:
552 raise self.outer
553
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000554 class SuppressExc:
555 def __enter__(self):
556 return self
557 def __exit__(self, *exc_details):
558 type(self).saved_details = exc_details
559 return True
560
561 try:
562 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000563 with RaiseExcWithContext(KeyError, AttributeError):
564 with SuppressExc():
565 with RaiseExc(ValueError):
566 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000567 except IndexError as exc:
568 self.assertIsInstance(exc.__context__, KeyError)
569 self.assertIsInstance(exc.__context__.__context__, AttributeError)
570 # Inner exceptions were suppressed
571 self.assertIsNone(exc.__context__.__context__.__context__)
572 else:
573 self.fail("Expected IndexError, but no exception was raised")
574 # Check the inner exceptions
575 inner_exc = SuppressExc.saved_details[1]
576 self.assertIsInstance(inner_exc, ValueError)
577 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
578
579 def test_exit_exception_chaining(self):
580 # Ensure exception chaining matches the reference behaviour
581 def raise_exc(exc):
582 raise exc
583
584 saved_details = None
585 def suppress_exc(*exc_details):
586 nonlocal saved_details
587 saved_details = exc_details
588 return True
589
590 try:
591 with ExitStack() as stack:
592 stack.callback(raise_exc, IndexError)
593 stack.callback(raise_exc, KeyError)
594 stack.callback(raise_exc, AttributeError)
595 stack.push(suppress_exc)
596 stack.callback(raise_exc, ValueError)
597 1 / 0
598 except IndexError as exc:
599 self.assertIsInstance(exc.__context__, KeyError)
600 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000601 # Inner exceptions were suppressed
602 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000603 else:
604 self.fail("Expected IndexError, but no exception was raised")
605 # Check the inner exceptions
606 inner_exc = saved_details[1]
607 self.assertIsInstance(inner_exc, ValueError)
608 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
609
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000610 def test_exit_exception_non_suppressing(self):
611 # http://bugs.python.org/issue19092
612 def raise_exc(exc):
613 raise exc
614
615 def suppress_exc(*exc_details):
616 return True
617
618 try:
619 with ExitStack() as stack:
620 stack.callback(lambda: None)
621 stack.callback(raise_exc, IndexError)
622 except Exception as exc:
623 self.assertIsInstance(exc, IndexError)
624 else:
625 self.fail("Expected IndexError, but no exception was raised")
626
627 try:
628 with ExitStack() as stack:
629 stack.callback(raise_exc, KeyError)
630 stack.push(suppress_exc)
631 stack.callback(raise_exc, IndexError)
632 except Exception as exc:
633 self.assertIsInstance(exc, KeyError)
634 else:
635 self.fail("Expected KeyError, but no exception was raised")
636
Nick Coghlan09761e72014-01-22 22:24:46 +1000637 def test_exit_exception_with_correct_context(self):
638 # http://bugs.python.org/issue20317
639 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000640 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000641 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000642 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000643 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000644 raise exc
645
646 exc1 = Exception(1)
647 exc2 = Exception(2)
648 exc3 = Exception(3)
649 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000650
651 # The contextmanager already fixes the context, so prior to the
652 # fix, ExitStack would try to fix it *again* and get into an
653 # infinite self-referential loop
654 try:
655 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000656 stack.enter_context(gets_the_context_right(exc4))
657 stack.enter_context(gets_the_context_right(exc3))
658 stack.enter_context(gets_the_context_right(exc2))
659 raise exc1
660 except Exception as exc:
661 self.assertIs(exc, exc4)
662 self.assertIs(exc.__context__, exc3)
663 self.assertIs(exc.__context__.__context__, exc2)
664 self.assertIs(exc.__context__.__context__.__context__, exc1)
665 self.assertIsNone(
666 exc.__context__.__context__.__context__.__context__)
667
668 def test_exit_exception_with_existing_context(self):
669 # Addresses a lack of test coverage discovered after checking in a
670 # fix for issue 20317 that still contained debugging code.
671 def raise_nested(inner_exc, outer_exc):
672 try:
673 raise inner_exc
674 finally:
675 raise outer_exc
676 exc1 = Exception(1)
677 exc2 = Exception(2)
678 exc3 = Exception(3)
679 exc4 = Exception(4)
680 exc5 = Exception(5)
681 try:
682 with ExitStack() as stack:
683 stack.callback(raise_nested, exc4, exc5)
684 stack.callback(raise_nested, exc2, exc3)
685 raise exc1
686 except Exception as exc:
687 self.assertIs(exc, exc5)
688 self.assertIs(exc.__context__, exc4)
689 self.assertIs(exc.__context__.__context__, exc3)
690 self.assertIs(exc.__context__.__context__.__context__, exc2)
691 self.assertIs(
692 exc.__context__.__context__.__context__.__context__, exc1)
693 self.assertIsNone(
694 exc.__context__.__context__.__context__.__context__.__context__)
695
Nick Coghlan09761e72014-01-22 22:24:46 +1000696
697
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000698 def test_body_exception_suppress(self):
699 def suppress_exc(*exc_details):
700 return True
701 try:
702 with ExitStack() as stack:
703 stack.push(suppress_exc)
704 1/0
705 except IndexError as exc:
706 self.fail("Expected no exception, got IndexError")
707
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000708 def test_exit_exception_chaining_suppress(self):
709 with ExitStack() as stack:
710 stack.push(lambda *exc: True)
711 stack.push(lambda *exc: 1/0)
712 stack.push(lambda *exc: {}[1])
713
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000714 def test_excessive_nesting(self):
715 # The original implementation would die with RecursionError here
716 with ExitStack() as stack:
717 for i in range(10000):
718 stack.callback(int)
719
Nick Coghlan3267a302012-05-21 22:54:43 +1000720 def test_instance_bypass(self):
721 class Example(object): pass
722 cm = Example()
723 cm.__exit__ = object()
724 stack = ExitStack()
725 self.assertRaises(AttributeError, stack.enter_context, cm)
726 stack.push(cm)
727 self.assertIs(stack._exit_callbacks[-1], cm)
728
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700729class TestRedirectStdout(unittest.TestCase):
730
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000731 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000732 def test_instance_docs(self):
733 # Issue 19330: ensure context manager instances have good docstrings
734 cm_docstring = redirect_stdout.__doc__
735 obj = redirect_stdout(None)
736 self.assertEqual(obj.__doc__, cm_docstring)
737
Nick Coghlan8e113b42013-11-03 17:00:51 +1000738 def test_no_redirect_in_init(self):
739 orig_stdout = sys.stdout
740 redirect_stdout(None)
741 self.assertIs(sys.stdout, orig_stdout)
742
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700743 def test_redirect_to_string_io(self):
744 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000745 msg = "Consider an API like help(), which prints directly to stdout"
Nick Coghlan8e113b42013-11-03 17:00:51 +1000746 orig_stdout = sys.stdout
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700747 with redirect_stdout(f):
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000748 print(msg)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000749 self.assertIs(sys.stdout, orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000750 s = f.getvalue().strip()
751 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000752
Nick Coghlan8608d262013-10-20 00:30:51 +1000753 def test_enter_result_is_target(self):
754 f = io.StringIO()
755 with redirect_stdout(f) as enter_result:
756 self.assertIs(enter_result, f)
757
758 def test_cm_is_reusable(self):
759 f = io.StringIO()
760 write_to_f = redirect_stdout(f)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000761 orig_stdout = sys.stdout
Nick Coghlan8608d262013-10-20 00:30:51 +1000762 with write_to_f:
763 print("Hello", end=" ")
764 with write_to_f:
765 print("World!")
Nick Coghlan8e113b42013-11-03 17:00:51 +1000766 self.assertIs(sys.stdout, orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000767 s = f.getvalue()
768 self.assertEqual(s, "Hello World!\n")
769
Nick Coghlan8e113b42013-11-03 17:00:51 +1000770 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000771 f = io.StringIO()
772 write_to_f = redirect_stdout(f)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000773 orig_stdout = sys.stdout
774 with write_to_f:
775 print("Hello", end=" ")
Nick Coghlan8608d262013-10-20 00:30:51 +1000776 with write_to_f:
Nick Coghlan8e113b42013-11-03 17:00:51 +1000777 print("World!")
778 self.assertIs(sys.stdout, orig_stdout)
779 s = f.getvalue()
780 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000781
782
Nick Coghlan240f86d2013-10-17 23:40:57 +1000783class TestSuppress(unittest.TestCase):
784
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000785 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000786 def test_instance_docs(self):
787 # Issue 19330: ensure context manager instances have good docstrings
788 cm_docstring = suppress.__doc__
789 obj = suppress()
790 self.assertEqual(obj.__doc__, cm_docstring)
791
Nick Coghlan8608d262013-10-20 00:30:51 +1000792 def test_no_result_from_enter(self):
793 with suppress(ValueError) as enter_result:
794 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000795
Nick Coghlan8608d262013-10-20 00:30:51 +1000796 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000797 with suppress(ValueError):
798 self.assertEqual(pow(2, 5), 32)
799
800 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000801 with suppress(TypeError):
802 len(5)
803
Nick Coghlan059def52013-10-26 18:08:15 +1000804 def test_exception_hierarchy(self):
805 with suppress(LookupError):
806 'Hello'[50]
807
808 def test_other_exception(self):
809 with self.assertRaises(ZeroDivisionError):
810 with suppress(TypeError):
811 1/0
812
813 def test_no_args(self):
814 with self.assertRaises(ZeroDivisionError):
815 with suppress():
816 1/0
817
Nick Coghlan240f86d2013-10-17 23:40:57 +1000818 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000819 with suppress(ZeroDivisionError, TypeError):
820 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000821 with suppress(ZeroDivisionError, TypeError):
822 len(5)
823
Nick Coghlan8608d262013-10-20 00:30:51 +1000824 def test_cm_is_reentrant(self):
825 ignore_exceptions = suppress(Exception)
826 with ignore_exceptions:
827 pass
828 with ignore_exceptions:
829 len(5)
830 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +1000831 with ignore_exceptions: # Check nested usage
832 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +0000833 outer_continued = True
834 1/0
835 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +1000836
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000837if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400838 unittest.main()