blob: c52066be3cc8098d9be3c3c613baf4d682334e3c [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
114
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000115class ClosingTestCase(unittest.TestCase):
116
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000117 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000118 def test_instance_docs(self):
119 # Issue 19330: ensure context manager instances have good docstrings
120 cm_docstring = closing.__doc__
121 obj = closing(None)
122 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000123
124 def test_closing(self):
125 state = []
126 class C:
127 def close(self):
128 state.append(1)
129 x = C()
130 self.assertEqual(state, [])
131 with closing(x) as y:
132 self.assertEqual(x, y)
133 self.assertEqual(state, [1])
134
135 def test_closing_error(self):
136 state = []
137 class C:
138 def close(self):
139 state.append(1)
140 x = C()
141 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000142 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000143 with closing(x) as y:
144 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000145 1 / 0
146 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000147
148class FileContextTestCase(unittest.TestCase):
149
150 def testWithOpen(self):
151 tfn = tempfile.mktemp()
152 try:
153 f = None
154 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000155 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000156 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000157 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000158 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000159 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000160 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000161 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000162 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000163 1 / 0
164 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000165 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000166 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000167
Victor Stinner45df8202010-04-28 22:31:17 +0000168@unittest.skipUnless(threading, 'Threading required for this test.')
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000169class LockContextTestCase(unittest.TestCase):
170
171 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000172 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000173 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000174 self.assertTrue(locked())
175 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000176 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000177 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000178 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000179 1 / 0
180 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000181
182 def testWithLock(self):
183 lock = threading.Lock()
184 self.boilerPlate(lock, lock.locked)
185
186 def testWithRLock(self):
187 lock = threading.RLock()
188 self.boilerPlate(lock, lock._is_owned)
189
190 def testWithCondition(self):
191 lock = threading.Condition()
192 def locked():
193 return lock._is_owned()
194 self.boilerPlate(lock, locked)
195
196 def testWithSemaphore(self):
197 lock = threading.Semaphore()
198 def locked():
199 if lock.acquire(False):
200 lock.release()
201 return False
202 else:
203 return True
204 self.boilerPlate(lock, locked)
205
206 def testWithBoundedSemaphore(self):
207 lock = threading.BoundedSemaphore()
208 def locked():
209 if lock.acquire(False):
210 lock.release()
211 return False
212 else:
213 return True
214 self.boilerPlate(lock, locked)
215
Michael Foordb3a89842010-06-30 12:17:50 +0000216
217class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000218 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000219 started = False
220 exc = None
221 catch = False
222
223 def __enter__(self):
224 self.started = True
225 return self
226
227 def __exit__(self, *exc):
228 self.exc = exc
229 return self.catch
230
231
232class TestContextDecorator(unittest.TestCase):
233
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000234 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000235 def test_instance_docs(self):
236 # Issue 19330: ensure context manager instances have good docstrings
237 cm_docstring = mycontext.__doc__
238 obj = mycontext()
239 self.assertEqual(obj.__doc__, cm_docstring)
240
Michael Foordb3a89842010-06-30 12:17:50 +0000241 def test_contextdecorator(self):
242 context = mycontext()
243 with context as result:
244 self.assertIs(result, context)
245 self.assertTrue(context.started)
246
247 self.assertEqual(context.exc, (None, None, None))
248
249
250 def test_contextdecorator_with_exception(self):
251 context = mycontext()
252
Ezio Melottied3a7d22010-12-01 02:32:32 +0000253 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000254 with context:
255 raise NameError('foo')
256 self.assertIsNotNone(context.exc)
257 self.assertIs(context.exc[0], NameError)
258
259 context = mycontext()
260 context.catch = True
261 with context:
262 raise NameError('foo')
263 self.assertIsNotNone(context.exc)
264 self.assertIs(context.exc[0], NameError)
265
266
267 def test_decorator(self):
268 context = mycontext()
269
270 @context
271 def test():
272 self.assertIsNone(context.exc)
273 self.assertTrue(context.started)
274 test()
275 self.assertEqual(context.exc, (None, None, None))
276
277
278 def test_decorator_with_exception(self):
279 context = mycontext()
280
281 @context
282 def test():
283 self.assertIsNone(context.exc)
284 self.assertTrue(context.started)
285 raise NameError('foo')
286
Ezio Melottied3a7d22010-12-01 02:32:32 +0000287 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000288 test()
289 self.assertIsNotNone(context.exc)
290 self.assertIs(context.exc[0], NameError)
291
292
293 def test_decorating_method(self):
294 context = mycontext()
295
296 class Test(object):
297
298 @context
299 def method(self, a, b, c=None):
300 self.a = a
301 self.b = b
302 self.c = c
303
304 # these tests are for argument passing when used as a decorator
305 test = Test()
306 test.method(1, 2)
307 self.assertEqual(test.a, 1)
308 self.assertEqual(test.b, 2)
309 self.assertEqual(test.c, None)
310
311 test = Test()
312 test.method('a', 'b', 'c')
313 self.assertEqual(test.a, 'a')
314 self.assertEqual(test.b, 'b')
315 self.assertEqual(test.c, 'c')
316
317 test = Test()
318 test.method(a=1, b=2)
319 self.assertEqual(test.a, 1)
320 self.assertEqual(test.b, 2)
321
322
323 def test_typo_enter(self):
324 class mycontext(ContextDecorator):
325 def __unter__(self):
326 pass
327 def __exit__(self, *exc):
328 pass
329
330 with self.assertRaises(AttributeError):
331 with mycontext():
332 pass
333
334
335 def test_typo_exit(self):
336 class mycontext(ContextDecorator):
337 def __enter__(self):
338 pass
339 def __uxit__(self, *exc):
340 pass
341
342 with self.assertRaises(AttributeError):
343 with mycontext():
344 pass
345
346
347 def test_contextdecorator_as_mixin(self):
348 class somecontext(object):
349 started = False
350 exc = None
351
352 def __enter__(self):
353 self.started = True
354 return self
355
356 def __exit__(self, *exc):
357 self.exc = exc
358
359 class mycontext(somecontext, ContextDecorator):
360 pass
361
362 context = mycontext()
363 @context
364 def test():
365 self.assertIsNone(context.exc)
366 self.assertTrue(context.started)
367 test()
368 self.assertEqual(context.exc, (None, None, None))
369
370
371 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000372 @contextmanager
373 def woohoo(y):
374 state.append(y)
375 yield
376 state.append(999)
377
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000378 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000379 @woohoo(1)
380 def test(x):
381 self.assertEqual(state, [1])
382 state.append(x)
383 test('something')
384 self.assertEqual(state, [1, 'something', 999])
385
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000386 # Issue #11647: Ensure the decorated function is 'reusable'
387 state = []
388 test('something else')
389 self.assertEqual(state, [1, 'something else', 999])
390
Michael Foordb3a89842010-06-30 12:17:50 +0000391
Nick Coghlan3267a302012-05-21 22:54:43 +1000392class TestExitStack(unittest.TestCase):
393
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000394 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000395 def test_instance_docs(self):
396 # Issue 19330: ensure context manager instances have good docstrings
397 cm_docstring = ExitStack.__doc__
398 obj = ExitStack()
399 self.assertEqual(obj.__doc__, cm_docstring)
400
Nick Coghlan3267a302012-05-21 22:54:43 +1000401 def test_no_resources(self):
402 with ExitStack():
403 pass
404
405 def test_callback(self):
406 expected = [
407 ((), {}),
408 ((1,), {}),
409 ((1,2), {}),
410 ((), dict(example=1)),
411 ((1,), dict(example=1)),
412 ((1,2), dict(example=1)),
413 ]
414 result = []
415 def _exit(*args, **kwds):
416 """Test metadata propagation"""
417 result.append((args, kwds))
418 with ExitStack() as stack:
419 for args, kwds in reversed(expected):
420 if args and kwds:
421 f = stack.callback(_exit, *args, **kwds)
422 elif args:
423 f = stack.callback(_exit, *args)
424 elif kwds:
425 f = stack.callback(_exit, **kwds)
426 else:
427 f = stack.callback(_exit)
428 self.assertIs(f, _exit)
429 for wrapper in stack._exit_callbacks:
430 self.assertIs(wrapper.__wrapped__, _exit)
431 self.assertNotEqual(wrapper.__name__, _exit.__name__)
432 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
433 self.assertEqual(result, expected)
434
435 def test_push(self):
436 exc_raised = ZeroDivisionError
437 def _expect_exc(exc_type, exc, exc_tb):
438 self.assertIs(exc_type, exc_raised)
439 def _suppress_exc(*exc_details):
440 return True
441 def _expect_ok(exc_type, exc, exc_tb):
442 self.assertIsNone(exc_type)
443 self.assertIsNone(exc)
444 self.assertIsNone(exc_tb)
445 class ExitCM(object):
446 def __init__(self, check_exc):
447 self.check_exc = check_exc
448 def __enter__(self):
449 self.fail("Should not be called!")
450 def __exit__(self, *exc_details):
451 self.check_exc(*exc_details)
452 with ExitStack() as stack:
453 stack.push(_expect_ok)
454 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
455 cm = ExitCM(_expect_ok)
456 stack.push(cm)
457 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
458 stack.push(_suppress_exc)
459 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
460 cm = ExitCM(_expect_exc)
461 stack.push(cm)
462 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
463 stack.push(_expect_exc)
464 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
465 stack.push(_expect_exc)
466 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
467 1/0
468
469 def test_enter_context(self):
470 class TestCM(object):
471 def __enter__(self):
472 result.append(1)
473 def __exit__(self, *exc_details):
474 result.append(3)
475
476 result = []
477 cm = TestCM()
478 with ExitStack() as stack:
479 @stack.callback # Registered first => cleaned up last
480 def _exit():
481 result.append(4)
482 self.assertIsNotNone(_exit)
483 stack.enter_context(cm)
484 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
485 result.append(2)
486 self.assertEqual(result, [1, 2, 3, 4])
487
488 def test_close(self):
489 result = []
490 with ExitStack() as stack:
491 @stack.callback
492 def _exit():
493 result.append(1)
494 self.assertIsNotNone(_exit)
495 stack.close()
496 result.append(2)
497 self.assertEqual(result, [1, 2])
498
499 def test_pop_all(self):
500 result = []
501 with ExitStack() as stack:
502 @stack.callback
503 def _exit():
504 result.append(3)
505 self.assertIsNotNone(_exit)
506 new_stack = stack.pop_all()
507 result.append(1)
508 result.append(2)
509 new_stack.close()
510 self.assertEqual(result, [1, 2, 3])
511
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000512 def test_exit_raise(self):
513 with self.assertRaises(ZeroDivisionError):
514 with ExitStack() as stack:
515 stack.push(lambda *exc: False)
516 1/0
517
518 def test_exit_suppress(self):
519 with ExitStack() as stack:
520 stack.push(lambda *exc: True)
521 1/0
522
523 def test_exit_exception_chaining_reference(self):
524 # Sanity check to make sure that ExitStack chaining matches
525 # actual nested with statements
526 class RaiseExc:
527 def __init__(self, exc):
528 self.exc = exc
529 def __enter__(self):
530 return self
531 def __exit__(self, *exc_details):
532 raise self.exc
533
Nick Coghlan77452fc2012-06-01 22:48:32 +1000534 class RaiseExcWithContext:
535 def __init__(self, outer, inner):
536 self.outer = outer
537 self.inner = inner
538 def __enter__(self):
539 return self
540 def __exit__(self, *exc_details):
541 try:
542 raise self.inner
543 except:
544 raise self.outer
545
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000546 class SuppressExc:
547 def __enter__(self):
548 return self
549 def __exit__(self, *exc_details):
550 type(self).saved_details = exc_details
551 return True
552
553 try:
554 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000555 with RaiseExcWithContext(KeyError, AttributeError):
556 with SuppressExc():
557 with RaiseExc(ValueError):
558 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000559 except IndexError as exc:
560 self.assertIsInstance(exc.__context__, KeyError)
561 self.assertIsInstance(exc.__context__.__context__, AttributeError)
562 # Inner exceptions were suppressed
563 self.assertIsNone(exc.__context__.__context__.__context__)
564 else:
565 self.fail("Expected IndexError, but no exception was raised")
566 # Check the inner exceptions
567 inner_exc = SuppressExc.saved_details[1]
568 self.assertIsInstance(inner_exc, ValueError)
569 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
570
571 def test_exit_exception_chaining(self):
572 # Ensure exception chaining matches the reference behaviour
573 def raise_exc(exc):
574 raise exc
575
576 saved_details = None
577 def suppress_exc(*exc_details):
578 nonlocal saved_details
579 saved_details = exc_details
580 return True
581
582 try:
583 with ExitStack() as stack:
584 stack.callback(raise_exc, IndexError)
585 stack.callback(raise_exc, KeyError)
586 stack.callback(raise_exc, AttributeError)
587 stack.push(suppress_exc)
588 stack.callback(raise_exc, ValueError)
589 1 / 0
590 except IndexError as exc:
591 self.assertIsInstance(exc.__context__, KeyError)
592 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000593 # Inner exceptions were suppressed
594 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000595 else:
596 self.fail("Expected IndexError, but no exception was raised")
597 # Check the inner exceptions
598 inner_exc = saved_details[1]
599 self.assertIsInstance(inner_exc, ValueError)
600 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
601
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000602 def test_exit_exception_non_suppressing(self):
603 # http://bugs.python.org/issue19092
604 def raise_exc(exc):
605 raise exc
606
607 def suppress_exc(*exc_details):
608 return True
609
610 try:
611 with ExitStack() as stack:
612 stack.callback(lambda: None)
613 stack.callback(raise_exc, IndexError)
614 except Exception as exc:
615 self.assertIsInstance(exc, IndexError)
616 else:
617 self.fail("Expected IndexError, but no exception was raised")
618
619 try:
620 with ExitStack() as stack:
621 stack.callback(raise_exc, KeyError)
622 stack.push(suppress_exc)
623 stack.callback(raise_exc, IndexError)
624 except Exception as exc:
625 self.assertIsInstance(exc, KeyError)
626 else:
627 self.fail("Expected KeyError, but no exception was raised")
628
Nick Coghlan09761e72014-01-22 22:24:46 +1000629 def test_exit_exception_with_correct_context(self):
630 # http://bugs.python.org/issue20317
631 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000632 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000633 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000634 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000635 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000636 raise exc
637
638 exc1 = Exception(1)
639 exc2 = Exception(2)
640 exc3 = Exception(3)
641 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000642
643 # The contextmanager already fixes the context, so prior to the
644 # fix, ExitStack would try to fix it *again* and get into an
645 # infinite self-referential loop
646 try:
647 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000648 stack.enter_context(gets_the_context_right(exc4))
649 stack.enter_context(gets_the_context_right(exc3))
650 stack.enter_context(gets_the_context_right(exc2))
651 raise exc1
652 except Exception as exc:
653 self.assertIs(exc, exc4)
654 self.assertIs(exc.__context__, exc3)
655 self.assertIs(exc.__context__.__context__, exc2)
656 self.assertIs(exc.__context__.__context__.__context__, exc1)
657 self.assertIsNone(
658 exc.__context__.__context__.__context__.__context__)
659
660 def test_exit_exception_with_existing_context(self):
661 # Addresses a lack of test coverage discovered after checking in a
662 # fix for issue 20317 that still contained debugging code.
663 def raise_nested(inner_exc, outer_exc):
664 try:
665 raise inner_exc
666 finally:
667 raise outer_exc
668 exc1 = Exception(1)
669 exc2 = Exception(2)
670 exc3 = Exception(3)
671 exc4 = Exception(4)
672 exc5 = Exception(5)
673 try:
674 with ExitStack() as stack:
675 stack.callback(raise_nested, exc4, exc5)
676 stack.callback(raise_nested, exc2, exc3)
677 raise exc1
678 except Exception as exc:
679 self.assertIs(exc, exc5)
680 self.assertIs(exc.__context__, exc4)
681 self.assertIs(exc.__context__.__context__, exc3)
682 self.assertIs(exc.__context__.__context__.__context__, exc2)
683 self.assertIs(
684 exc.__context__.__context__.__context__.__context__, exc1)
685 self.assertIsNone(
686 exc.__context__.__context__.__context__.__context__.__context__)
687
Nick Coghlan09761e72014-01-22 22:24:46 +1000688
689
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000690 def test_body_exception_suppress(self):
691 def suppress_exc(*exc_details):
692 return True
693 try:
694 with ExitStack() as stack:
695 stack.push(suppress_exc)
696 1/0
697 except IndexError as exc:
698 self.fail("Expected no exception, got IndexError")
699
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000700 def test_exit_exception_chaining_suppress(self):
701 with ExitStack() as stack:
702 stack.push(lambda *exc: True)
703 stack.push(lambda *exc: 1/0)
704 stack.push(lambda *exc: {}[1])
705
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000706 def test_excessive_nesting(self):
707 # The original implementation would die with RecursionError here
708 with ExitStack() as stack:
709 for i in range(10000):
710 stack.callback(int)
711
Nick Coghlan3267a302012-05-21 22:54:43 +1000712 def test_instance_bypass(self):
713 class Example(object): pass
714 cm = Example()
715 cm.__exit__ = object()
716 stack = ExitStack()
717 self.assertRaises(AttributeError, stack.enter_context, cm)
718 stack.push(cm)
719 self.assertIs(stack._exit_callbacks[-1], cm)
720
Berker Peksagbb44fe02014-11-28 23:28:06 +0200721
722class TestRedirectStream:
723
724 redirect_stream = None
725 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700726
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000727 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000728 def test_instance_docs(self):
729 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200730 cm_docstring = self.redirect_stream.__doc__
731 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000732 self.assertEqual(obj.__doc__, cm_docstring)
733
Nick Coghlan8e113b42013-11-03 17:00:51 +1000734 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200735 orig_stdout = getattr(sys, self.orig_stream)
736 self.redirect_stream(None)
737 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000738
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700739 def test_redirect_to_string_io(self):
740 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000741 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200742 orig_stdout = getattr(sys, self.orig_stream)
743 with self.redirect_stream(f):
744 print(msg, file=getattr(sys, self.orig_stream))
745 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000746 s = f.getvalue().strip()
747 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000748
Nick Coghlan8608d262013-10-20 00:30:51 +1000749 def test_enter_result_is_target(self):
750 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200751 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000752 self.assertIs(enter_result, f)
753
754 def test_cm_is_reusable(self):
755 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200756 write_to_f = self.redirect_stream(f)
757 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000758 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200759 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000760 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200761 print("World!", file=getattr(sys, self.orig_stream))
762 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000763 s = f.getvalue()
764 self.assertEqual(s, "Hello World!\n")
765
Nick Coghlan8e113b42013-11-03 17:00:51 +1000766 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000767 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200768 write_to_f = self.redirect_stream(f)
769 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000770 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200771 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000772 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200773 print("World!", file=getattr(sys, self.orig_stream))
774 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000775 s = f.getvalue()
776 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000777
778
Berker Peksagbb44fe02014-11-28 23:28:06 +0200779class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
780
781 redirect_stream = redirect_stdout
782 orig_stream = "stdout"
783
784
785class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
786
787 redirect_stream = redirect_stderr
788 orig_stream = "stderr"
789
790
Nick Coghlan240f86d2013-10-17 23:40:57 +1000791class TestSuppress(unittest.TestCase):
792
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000793 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000794 def test_instance_docs(self):
795 # Issue 19330: ensure context manager instances have good docstrings
796 cm_docstring = suppress.__doc__
797 obj = suppress()
798 self.assertEqual(obj.__doc__, cm_docstring)
799
Nick Coghlan8608d262013-10-20 00:30:51 +1000800 def test_no_result_from_enter(self):
801 with suppress(ValueError) as enter_result:
802 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000803
Nick Coghlan8608d262013-10-20 00:30:51 +1000804 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000805 with suppress(ValueError):
806 self.assertEqual(pow(2, 5), 32)
807
808 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000809 with suppress(TypeError):
810 len(5)
811
Nick Coghlan059def52013-10-26 18:08:15 +1000812 def test_exception_hierarchy(self):
813 with suppress(LookupError):
814 'Hello'[50]
815
816 def test_other_exception(self):
817 with self.assertRaises(ZeroDivisionError):
818 with suppress(TypeError):
819 1/0
820
821 def test_no_args(self):
822 with self.assertRaises(ZeroDivisionError):
823 with suppress():
824 1/0
825
Nick Coghlan240f86d2013-10-17 23:40:57 +1000826 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000827 with suppress(ZeroDivisionError, TypeError):
828 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000829 with suppress(ZeroDivisionError, TypeError):
830 len(5)
831
Nick Coghlan8608d262013-10-20 00:30:51 +1000832 def test_cm_is_reentrant(self):
833 ignore_exceptions = suppress(Exception)
834 with ignore_exceptions:
835 pass
836 with ignore_exceptions:
837 len(5)
838 with ignore_exceptions:
839 1/0
840 with ignore_exceptions: # Check nested usage
841 len(5)
842
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000843if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400844 unittest.main()