blob: adafc9fd4f61c36e89ed753edad972fe530fc156 [file] [log] [blame]
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001"""Unit tests for contextlib.py, and other context managers."""
2
Raymond Hettinger088cbf22013-10-10 00:46:57 -07003import io
R. David Murray378c0cf2010-02-24 01:46:21 +00004import sys
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00005import tempfile
6import unittest
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00007from contextlib import * # Tests __all__
Benjamin Petersonee8712c2008-05-20 21:35:26 +00008from test import support
Victor Stinner45df8202010-04-28 22:31:17 +00009try:
10 import threading
11except ImportError:
12 threading = None
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000013
Florent Xicluna41fe6152010-04-02 18:52:12 +000014
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000015class ContextManagerTestCase(unittest.TestCase):
16
17 def test_contextmanager_plain(self):
18 state = []
19 @contextmanager
20 def woohoo():
21 state.append(1)
22 yield 42
23 state.append(999)
24 with woohoo() as x:
25 self.assertEqual(state, [1])
26 self.assertEqual(x, 42)
27 state.append(x)
28 self.assertEqual(state, [1, 42, 999])
29
30 def test_contextmanager_finally(self):
31 state = []
32 @contextmanager
33 def woohoo():
34 state.append(1)
35 try:
36 yield 42
37 finally:
38 state.append(999)
Florent Xicluna41fe6152010-04-02 18:52:12 +000039 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000040 with woohoo() as x:
41 self.assertEqual(state, [1])
42 self.assertEqual(x, 42)
43 state.append(x)
44 raise ZeroDivisionError()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000045 self.assertEqual(state, [1, 42, 999])
46
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000047 def test_contextmanager_no_reraise(self):
48 @contextmanager
49 def whee():
50 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000051 ctx = whee()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000052 ctx.__enter__()
53 # Calling __exit__ should not result in an exception
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000054 self.assertFalse(ctx.__exit__(TypeError, TypeError("foo"), None))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000055
56 def test_contextmanager_trap_yield_after_throw(self):
57 @contextmanager
58 def whoo():
59 try:
60 yield
61 except:
62 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000063 ctx = whoo()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000064 ctx.__enter__()
65 self.assertRaises(
66 RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
67 )
68
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000069 def test_contextmanager_except(self):
70 state = []
71 @contextmanager
72 def woohoo():
73 state.append(1)
74 try:
75 yield 42
Guido van Rossumb940e112007-01-10 16:19:56 +000076 except ZeroDivisionError as e:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000077 state.append(e.args[0])
78 self.assertEqual(state, [1, 42, 999])
79 with woohoo() as x:
80 self.assertEqual(state, [1])
81 self.assertEqual(x, 42)
82 state.append(x)
83 raise ZeroDivisionError(999)
84 self.assertEqual(state, [1, 42, 999])
85
Yury Selivanov8170e8c2015-05-09 11:44:30 -040086 def test_contextmanager_except_stopiter(self):
87 stop_exc = StopIteration('spam')
88 @contextmanager
89 def woohoo():
90 yield
91 try:
Yury Selivanov68333392015-05-22 11:16:47 -040092 with self.assertWarnsRegex(PendingDeprecationWarning,
93 "StopIteration"):
94 with woohoo():
95 raise stop_exc
Yury Selivanov8170e8c2015-05-09 11:44:30 -040096 except Exception as ex:
97 self.assertIs(ex, stop_exc)
98 else:
99 self.fail('StopIteration was suppressed')
100
101 def test_contextmanager_except_pep479(self):
102 code = """\
103from __future__ import generator_stop
104from contextlib import contextmanager
105@contextmanager
106def woohoo():
107 yield
108"""
109 locals = {}
110 exec(code, locals, locals)
111 woohoo = locals['woohoo']
112
113 stop_exc = StopIteration('spam')
114 try:
115 with woohoo():
116 raise stop_exc
117 except Exception as ex:
118 self.assertIs(ex, stop_exc)
119 else:
120 self.fail('StopIteration was suppressed')
121
R. David Murray378c0cf2010-02-24 01:46:21 +0000122 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000123 def attribs(**kw):
124 def decorate(func):
125 for k,v in kw.items():
126 setattr(func,k,v)
127 return func
128 return decorate
129 @contextmanager
130 @attribs(foo='bar')
131 def baz(spam):
132 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000133 return baz
134
135 def test_contextmanager_attribs(self):
136 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000137 self.assertEqual(baz.__name__,'baz')
138 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000139
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000140 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000141 def test_contextmanager_doc_attrib(self):
142 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000143 self.assertEqual(baz.__doc__, "Whee!")
144
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000145 @support.requires_docstrings
146 def test_instance_docstring_given_cm_docstring(self):
147 baz = self._create_contextmanager_attribs()(None)
148 self.assertEqual(baz.__doc__, "Whee!")
149
150
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000151class ClosingTestCase(unittest.TestCase):
152
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000153 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000154 def test_instance_docs(self):
155 # Issue 19330: ensure context manager instances have good docstrings
156 cm_docstring = closing.__doc__
157 obj = closing(None)
158 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000159
160 def test_closing(self):
161 state = []
162 class C:
163 def close(self):
164 state.append(1)
165 x = C()
166 self.assertEqual(state, [])
167 with closing(x) as y:
168 self.assertEqual(x, y)
169 self.assertEqual(state, [1])
170
171 def test_closing_error(self):
172 state = []
173 class C:
174 def close(self):
175 state.append(1)
176 x = C()
177 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000178 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000179 with closing(x) as y:
180 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000181 1 / 0
182 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000183
184class FileContextTestCase(unittest.TestCase):
185
186 def testWithOpen(self):
187 tfn = tempfile.mktemp()
188 try:
189 f = None
190 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000191 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000192 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000193 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000194 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000195 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000196 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000197 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000198 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000199 1 / 0
200 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000201 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000202 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000203
Victor Stinner45df8202010-04-28 22:31:17 +0000204@unittest.skipUnless(threading, 'Threading required for this test.')
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000205class LockContextTestCase(unittest.TestCase):
206
207 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000208 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000209 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000210 self.assertTrue(locked())
211 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000212 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000213 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000214 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000215 1 / 0
216 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000217
218 def testWithLock(self):
219 lock = threading.Lock()
220 self.boilerPlate(lock, lock.locked)
221
222 def testWithRLock(self):
223 lock = threading.RLock()
224 self.boilerPlate(lock, lock._is_owned)
225
226 def testWithCondition(self):
227 lock = threading.Condition()
228 def locked():
229 return lock._is_owned()
230 self.boilerPlate(lock, locked)
231
232 def testWithSemaphore(self):
233 lock = threading.Semaphore()
234 def locked():
235 if lock.acquire(False):
236 lock.release()
237 return False
238 else:
239 return True
240 self.boilerPlate(lock, locked)
241
242 def testWithBoundedSemaphore(self):
243 lock = threading.BoundedSemaphore()
244 def locked():
245 if lock.acquire(False):
246 lock.release()
247 return False
248 else:
249 return True
250 self.boilerPlate(lock, locked)
251
Michael Foordb3a89842010-06-30 12:17:50 +0000252
253class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000254 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000255 started = False
256 exc = None
257 catch = False
258
259 def __enter__(self):
260 self.started = True
261 return self
262
263 def __exit__(self, *exc):
264 self.exc = exc
265 return self.catch
266
267
268class TestContextDecorator(unittest.TestCase):
269
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000270 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000271 def test_instance_docs(self):
272 # Issue 19330: ensure context manager instances have good docstrings
273 cm_docstring = mycontext.__doc__
274 obj = mycontext()
275 self.assertEqual(obj.__doc__, cm_docstring)
276
Michael Foordb3a89842010-06-30 12:17:50 +0000277 def test_contextdecorator(self):
278 context = mycontext()
279 with context as result:
280 self.assertIs(result, context)
281 self.assertTrue(context.started)
282
283 self.assertEqual(context.exc, (None, None, None))
284
285
286 def test_contextdecorator_with_exception(self):
287 context = mycontext()
288
Ezio Melottied3a7d22010-12-01 02:32:32 +0000289 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000290 with context:
291 raise NameError('foo')
292 self.assertIsNotNone(context.exc)
293 self.assertIs(context.exc[0], NameError)
294
295 context = mycontext()
296 context.catch = True
297 with context:
298 raise NameError('foo')
299 self.assertIsNotNone(context.exc)
300 self.assertIs(context.exc[0], NameError)
301
302
303 def test_decorator(self):
304 context = mycontext()
305
306 @context
307 def test():
308 self.assertIsNone(context.exc)
309 self.assertTrue(context.started)
310 test()
311 self.assertEqual(context.exc, (None, None, None))
312
313
314 def test_decorator_with_exception(self):
315 context = mycontext()
316
317 @context
318 def test():
319 self.assertIsNone(context.exc)
320 self.assertTrue(context.started)
321 raise NameError('foo')
322
Ezio Melottied3a7d22010-12-01 02:32:32 +0000323 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000324 test()
325 self.assertIsNotNone(context.exc)
326 self.assertIs(context.exc[0], NameError)
327
328
329 def test_decorating_method(self):
330 context = mycontext()
331
332 class Test(object):
333
334 @context
335 def method(self, a, b, c=None):
336 self.a = a
337 self.b = b
338 self.c = c
339
340 # these tests are for argument passing when used as a decorator
341 test = Test()
342 test.method(1, 2)
343 self.assertEqual(test.a, 1)
344 self.assertEqual(test.b, 2)
345 self.assertEqual(test.c, None)
346
347 test = Test()
348 test.method('a', 'b', 'c')
349 self.assertEqual(test.a, 'a')
350 self.assertEqual(test.b, 'b')
351 self.assertEqual(test.c, 'c')
352
353 test = Test()
354 test.method(a=1, b=2)
355 self.assertEqual(test.a, 1)
356 self.assertEqual(test.b, 2)
357
358
359 def test_typo_enter(self):
360 class mycontext(ContextDecorator):
361 def __unter__(self):
362 pass
363 def __exit__(self, *exc):
364 pass
365
366 with self.assertRaises(AttributeError):
367 with mycontext():
368 pass
369
370
371 def test_typo_exit(self):
372 class mycontext(ContextDecorator):
373 def __enter__(self):
374 pass
375 def __uxit__(self, *exc):
376 pass
377
378 with self.assertRaises(AttributeError):
379 with mycontext():
380 pass
381
382
383 def test_contextdecorator_as_mixin(self):
384 class somecontext(object):
385 started = False
386 exc = None
387
388 def __enter__(self):
389 self.started = True
390 return self
391
392 def __exit__(self, *exc):
393 self.exc = exc
394
395 class mycontext(somecontext, ContextDecorator):
396 pass
397
398 context = mycontext()
399 @context
400 def test():
401 self.assertIsNone(context.exc)
402 self.assertTrue(context.started)
403 test()
404 self.assertEqual(context.exc, (None, None, None))
405
406
407 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000408 @contextmanager
409 def woohoo(y):
410 state.append(y)
411 yield
412 state.append(999)
413
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000414 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000415 @woohoo(1)
416 def test(x):
417 self.assertEqual(state, [1])
418 state.append(x)
419 test('something')
420 self.assertEqual(state, [1, 'something', 999])
421
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000422 # Issue #11647: Ensure the decorated function is 'reusable'
423 state = []
424 test('something else')
425 self.assertEqual(state, [1, 'something else', 999])
426
Michael Foordb3a89842010-06-30 12:17:50 +0000427
Nick Coghlan3267a302012-05-21 22:54:43 +1000428class TestExitStack(unittest.TestCase):
429
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000430 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000431 def test_instance_docs(self):
432 # Issue 19330: ensure context manager instances have good docstrings
433 cm_docstring = ExitStack.__doc__
434 obj = ExitStack()
435 self.assertEqual(obj.__doc__, cm_docstring)
436
Nick Coghlan3267a302012-05-21 22:54:43 +1000437 def test_no_resources(self):
438 with ExitStack():
439 pass
440
441 def test_callback(self):
442 expected = [
443 ((), {}),
444 ((1,), {}),
445 ((1,2), {}),
446 ((), dict(example=1)),
447 ((1,), dict(example=1)),
448 ((1,2), dict(example=1)),
449 ]
450 result = []
451 def _exit(*args, **kwds):
452 """Test metadata propagation"""
453 result.append((args, kwds))
454 with ExitStack() as stack:
455 for args, kwds in reversed(expected):
456 if args and kwds:
457 f = stack.callback(_exit, *args, **kwds)
458 elif args:
459 f = stack.callback(_exit, *args)
460 elif kwds:
461 f = stack.callback(_exit, **kwds)
462 else:
463 f = stack.callback(_exit)
464 self.assertIs(f, _exit)
465 for wrapper in stack._exit_callbacks:
466 self.assertIs(wrapper.__wrapped__, _exit)
467 self.assertNotEqual(wrapper.__name__, _exit.__name__)
468 self.assertIsNone(wrapper.__doc__, _exit.__doc__)
469 self.assertEqual(result, expected)
470
471 def test_push(self):
472 exc_raised = ZeroDivisionError
473 def _expect_exc(exc_type, exc, exc_tb):
474 self.assertIs(exc_type, exc_raised)
475 def _suppress_exc(*exc_details):
476 return True
477 def _expect_ok(exc_type, exc, exc_tb):
478 self.assertIsNone(exc_type)
479 self.assertIsNone(exc)
480 self.assertIsNone(exc_tb)
481 class ExitCM(object):
482 def __init__(self, check_exc):
483 self.check_exc = check_exc
484 def __enter__(self):
485 self.fail("Should not be called!")
486 def __exit__(self, *exc_details):
487 self.check_exc(*exc_details)
488 with ExitStack() as stack:
489 stack.push(_expect_ok)
490 self.assertIs(stack._exit_callbacks[-1], _expect_ok)
491 cm = ExitCM(_expect_ok)
492 stack.push(cm)
493 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
494 stack.push(_suppress_exc)
495 self.assertIs(stack._exit_callbacks[-1], _suppress_exc)
496 cm = ExitCM(_expect_exc)
497 stack.push(cm)
498 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
499 stack.push(_expect_exc)
500 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
501 stack.push(_expect_exc)
502 self.assertIs(stack._exit_callbacks[-1], _expect_exc)
503 1/0
504
505 def test_enter_context(self):
506 class TestCM(object):
507 def __enter__(self):
508 result.append(1)
509 def __exit__(self, *exc_details):
510 result.append(3)
511
512 result = []
513 cm = TestCM()
514 with ExitStack() as stack:
515 @stack.callback # Registered first => cleaned up last
516 def _exit():
517 result.append(4)
518 self.assertIsNotNone(_exit)
519 stack.enter_context(cm)
520 self.assertIs(stack._exit_callbacks[-1].__self__, cm)
521 result.append(2)
522 self.assertEqual(result, [1, 2, 3, 4])
523
524 def test_close(self):
525 result = []
526 with ExitStack() as stack:
527 @stack.callback
528 def _exit():
529 result.append(1)
530 self.assertIsNotNone(_exit)
531 stack.close()
532 result.append(2)
533 self.assertEqual(result, [1, 2])
534
535 def test_pop_all(self):
536 result = []
537 with ExitStack() as stack:
538 @stack.callback
539 def _exit():
540 result.append(3)
541 self.assertIsNotNone(_exit)
542 new_stack = stack.pop_all()
543 result.append(1)
544 result.append(2)
545 new_stack.close()
546 self.assertEqual(result, [1, 2, 3])
547
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000548 def test_exit_raise(self):
549 with self.assertRaises(ZeroDivisionError):
550 with ExitStack() as stack:
551 stack.push(lambda *exc: False)
552 1/0
553
554 def test_exit_suppress(self):
555 with ExitStack() as stack:
556 stack.push(lambda *exc: True)
557 1/0
558
559 def test_exit_exception_chaining_reference(self):
560 # Sanity check to make sure that ExitStack chaining matches
561 # actual nested with statements
562 class RaiseExc:
563 def __init__(self, exc):
564 self.exc = exc
565 def __enter__(self):
566 return self
567 def __exit__(self, *exc_details):
568 raise self.exc
569
Nick Coghlan77452fc2012-06-01 22:48:32 +1000570 class RaiseExcWithContext:
571 def __init__(self, outer, inner):
572 self.outer = outer
573 self.inner = inner
574 def __enter__(self):
575 return self
576 def __exit__(self, *exc_details):
577 try:
578 raise self.inner
579 except:
580 raise self.outer
581
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000582 class SuppressExc:
583 def __enter__(self):
584 return self
585 def __exit__(self, *exc_details):
586 type(self).saved_details = exc_details
587 return True
588
589 try:
590 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000591 with RaiseExcWithContext(KeyError, AttributeError):
592 with SuppressExc():
593 with RaiseExc(ValueError):
594 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000595 except IndexError as exc:
596 self.assertIsInstance(exc.__context__, KeyError)
597 self.assertIsInstance(exc.__context__.__context__, AttributeError)
598 # Inner exceptions were suppressed
599 self.assertIsNone(exc.__context__.__context__.__context__)
600 else:
601 self.fail("Expected IndexError, but no exception was raised")
602 # Check the inner exceptions
603 inner_exc = SuppressExc.saved_details[1]
604 self.assertIsInstance(inner_exc, ValueError)
605 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
606
607 def test_exit_exception_chaining(self):
608 # Ensure exception chaining matches the reference behaviour
609 def raise_exc(exc):
610 raise exc
611
612 saved_details = None
613 def suppress_exc(*exc_details):
614 nonlocal saved_details
615 saved_details = exc_details
616 return True
617
618 try:
619 with ExitStack() as stack:
620 stack.callback(raise_exc, IndexError)
621 stack.callback(raise_exc, KeyError)
622 stack.callback(raise_exc, AttributeError)
623 stack.push(suppress_exc)
624 stack.callback(raise_exc, ValueError)
625 1 / 0
626 except IndexError as exc:
627 self.assertIsInstance(exc.__context__, KeyError)
628 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000629 # Inner exceptions were suppressed
630 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000631 else:
632 self.fail("Expected IndexError, but no exception was raised")
633 # Check the inner exceptions
634 inner_exc = saved_details[1]
635 self.assertIsInstance(inner_exc, ValueError)
636 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
637
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000638 def test_exit_exception_non_suppressing(self):
639 # http://bugs.python.org/issue19092
640 def raise_exc(exc):
641 raise exc
642
643 def suppress_exc(*exc_details):
644 return True
645
646 try:
647 with ExitStack() as stack:
648 stack.callback(lambda: None)
649 stack.callback(raise_exc, IndexError)
650 except Exception as exc:
651 self.assertIsInstance(exc, IndexError)
652 else:
653 self.fail("Expected IndexError, but no exception was raised")
654
655 try:
656 with ExitStack() as stack:
657 stack.callback(raise_exc, KeyError)
658 stack.push(suppress_exc)
659 stack.callback(raise_exc, IndexError)
660 except Exception as exc:
661 self.assertIsInstance(exc, KeyError)
662 else:
663 self.fail("Expected KeyError, but no exception was raised")
664
Nick Coghlan09761e72014-01-22 22:24:46 +1000665 def test_exit_exception_with_correct_context(self):
666 # http://bugs.python.org/issue20317
667 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000668 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000669 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000670 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000671 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000672 raise exc
673
674 exc1 = Exception(1)
675 exc2 = Exception(2)
676 exc3 = Exception(3)
677 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000678
679 # The contextmanager already fixes the context, so prior to the
680 # fix, ExitStack would try to fix it *again* and get into an
681 # infinite self-referential loop
682 try:
683 with ExitStack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000684 stack.enter_context(gets_the_context_right(exc4))
685 stack.enter_context(gets_the_context_right(exc3))
686 stack.enter_context(gets_the_context_right(exc2))
687 raise exc1
688 except Exception as exc:
689 self.assertIs(exc, exc4)
690 self.assertIs(exc.__context__, exc3)
691 self.assertIs(exc.__context__.__context__, exc2)
692 self.assertIs(exc.__context__.__context__.__context__, exc1)
693 self.assertIsNone(
694 exc.__context__.__context__.__context__.__context__)
695
696 def test_exit_exception_with_existing_context(self):
697 # Addresses a lack of test coverage discovered after checking in a
698 # fix for issue 20317 that still contained debugging code.
699 def raise_nested(inner_exc, outer_exc):
700 try:
701 raise inner_exc
702 finally:
703 raise outer_exc
704 exc1 = Exception(1)
705 exc2 = Exception(2)
706 exc3 = Exception(3)
707 exc4 = Exception(4)
708 exc5 = Exception(5)
709 try:
710 with ExitStack() as stack:
711 stack.callback(raise_nested, exc4, exc5)
712 stack.callback(raise_nested, exc2, exc3)
713 raise exc1
714 except Exception as exc:
715 self.assertIs(exc, exc5)
716 self.assertIs(exc.__context__, exc4)
717 self.assertIs(exc.__context__.__context__, exc3)
718 self.assertIs(exc.__context__.__context__.__context__, exc2)
719 self.assertIs(
720 exc.__context__.__context__.__context__.__context__, exc1)
721 self.assertIsNone(
722 exc.__context__.__context__.__context__.__context__.__context__)
723
Nick Coghlan09761e72014-01-22 22:24:46 +1000724
725
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000726 def test_body_exception_suppress(self):
727 def suppress_exc(*exc_details):
728 return True
729 try:
730 with ExitStack() as stack:
731 stack.push(suppress_exc)
732 1/0
733 except IndexError as exc:
734 self.fail("Expected no exception, got IndexError")
735
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000736 def test_exit_exception_chaining_suppress(self):
737 with ExitStack() as stack:
738 stack.push(lambda *exc: True)
739 stack.push(lambda *exc: 1/0)
740 stack.push(lambda *exc: {}[1])
741
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000742 def test_excessive_nesting(self):
743 # The original implementation would die with RecursionError here
744 with ExitStack() as stack:
745 for i in range(10000):
746 stack.callback(int)
747
Nick Coghlan3267a302012-05-21 22:54:43 +1000748 def test_instance_bypass(self):
749 class Example(object): pass
750 cm = Example()
751 cm.__exit__ = object()
752 stack = ExitStack()
753 self.assertRaises(AttributeError, stack.enter_context, cm)
754 stack.push(cm)
755 self.assertIs(stack._exit_callbacks[-1], cm)
756
Berker Peksagbb44fe02014-11-28 23:28:06 +0200757
758class TestRedirectStream:
759
760 redirect_stream = None
761 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700762
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000763 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000764 def test_instance_docs(self):
765 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200766 cm_docstring = self.redirect_stream.__doc__
767 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000768 self.assertEqual(obj.__doc__, cm_docstring)
769
Nick Coghlan8e113b42013-11-03 17:00:51 +1000770 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200771 orig_stdout = getattr(sys, self.orig_stream)
772 self.redirect_stream(None)
773 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000774
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700775 def test_redirect_to_string_io(self):
776 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000777 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200778 orig_stdout = getattr(sys, self.orig_stream)
779 with self.redirect_stream(f):
780 print(msg, file=getattr(sys, self.orig_stream))
781 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000782 s = f.getvalue().strip()
783 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000784
Nick Coghlan8608d262013-10-20 00:30:51 +1000785 def test_enter_result_is_target(self):
786 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200787 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000788 self.assertIs(enter_result, f)
789
790 def test_cm_is_reusable(self):
791 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200792 write_to_f = self.redirect_stream(f)
793 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000794 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200795 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000796 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200797 print("World!", file=getattr(sys, self.orig_stream))
798 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000799 s = f.getvalue()
800 self.assertEqual(s, "Hello World!\n")
801
Nick Coghlan8e113b42013-11-03 17:00:51 +1000802 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000803 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200804 write_to_f = self.redirect_stream(f)
805 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000806 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200807 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000808 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200809 print("World!", file=getattr(sys, self.orig_stream))
810 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000811 s = f.getvalue()
812 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000813
814
Berker Peksagbb44fe02014-11-28 23:28:06 +0200815class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
816
817 redirect_stream = redirect_stdout
818 orig_stream = "stdout"
819
820
821class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
822
823 redirect_stream = redirect_stderr
824 orig_stream = "stderr"
825
826
Nick Coghlan240f86d2013-10-17 23:40:57 +1000827class TestSuppress(unittest.TestCase):
828
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000829 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000830 def test_instance_docs(self):
831 # Issue 19330: ensure context manager instances have good docstrings
832 cm_docstring = suppress.__doc__
833 obj = suppress()
834 self.assertEqual(obj.__doc__, cm_docstring)
835
Nick Coghlan8608d262013-10-20 00:30:51 +1000836 def test_no_result_from_enter(self):
837 with suppress(ValueError) as enter_result:
838 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +1000839
Nick Coghlan8608d262013-10-20 00:30:51 +1000840 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000841 with suppress(ValueError):
842 self.assertEqual(pow(2, 5), 32)
843
844 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +1000845 with suppress(TypeError):
846 len(5)
847
Nick Coghlan059def52013-10-26 18:08:15 +1000848 def test_exception_hierarchy(self):
849 with suppress(LookupError):
850 'Hello'[50]
851
852 def test_other_exception(self):
853 with self.assertRaises(ZeroDivisionError):
854 with suppress(TypeError):
855 1/0
856
857 def test_no_args(self):
858 with self.assertRaises(ZeroDivisionError):
859 with suppress():
860 1/0
861
Nick Coghlan240f86d2013-10-17 23:40:57 +1000862 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000863 with suppress(ZeroDivisionError, TypeError):
864 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +1000865 with suppress(ZeroDivisionError, TypeError):
866 len(5)
867
Nick Coghlan8608d262013-10-20 00:30:51 +1000868 def test_cm_is_reentrant(self):
869 ignore_exceptions = suppress(Exception)
870 with ignore_exceptions:
871 pass
872 with ignore_exceptions:
873 len(5)
874 with ignore_exceptions:
875 1/0
876 with ignore_exceptions: # Check nested usage
877 len(5)
878
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000879if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400880 unittest.main()