blob: 2a44404a603e4db81c85707a41572bfa6951c8e9 [file] [log] [blame]
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001"""Unit tests for contextlib.py, and other context managers."""
2
Ilya Kulakov1aa094f2018-01-25 12:51:18 -08003import asyncio
Raymond Hettinger088cbf22013-10-10 00:46:57 -07004import io
R. David Murray378c0cf2010-02-24 01:46:21 +00005import sys
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00006import tempfile
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02007import threading
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00008import unittest
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00009from contextlib import * # Tests __all__
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Martin Teichmanndd0e0872018-01-28 05:17:46 +010011import weakref
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000012
Florent Xicluna41fe6152010-04-02 18:52:12 +000013
Brett Cannon9e080e02016-04-08 12:15:27 -070014class TestAbstractContextManager(unittest.TestCase):
15
16 def test_enter(self):
17 class DefaultEnter(AbstractContextManager):
18 def __exit__(self, *args):
19 super().__exit__(*args)
20
21 manager = DefaultEnter()
22 self.assertIs(manager.__enter__(), manager)
23
24 def test_exit_is_abstract(self):
25 class MissingExit(AbstractContextManager):
26 pass
27
28 with self.assertRaises(TypeError):
29 MissingExit()
30
31 def test_structural_subclassing(self):
32 class ManagerFromScratch:
33 def __enter__(self):
34 return self
35 def __exit__(self, exc_type, exc_value, traceback):
36 return None
37
38 self.assertTrue(issubclass(ManagerFromScratch, AbstractContextManager))
39
40 class DefaultEnter(AbstractContextManager):
41 def __exit__(self, *args):
42 super().__exit__(*args)
43
44 self.assertTrue(issubclass(DefaultEnter, AbstractContextManager))
45
Jelle Zijlstra57161aa2017-06-09 08:21:47 -070046 class NoEnter(ManagerFromScratch):
47 __enter__ = None
48
49 self.assertFalse(issubclass(NoEnter, AbstractContextManager))
50
51 class NoExit(ManagerFromScratch):
52 __exit__ = None
53
54 self.assertFalse(issubclass(NoExit, AbstractContextManager))
55
Brett Cannon9e080e02016-04-08 12:15:27 -070056
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000057class ContextManagerTestCase(unittest.TestCase):
58
59 def test_contextmanager_plain(self):
60 state = []
61 @contextmanager
62 def woohoo():
63 state.append(1)
64 yield 42
65 state.append(999)
66 with woohoo() as x:
67 self.assertEqual(state, [1])
68 self.assertEqual(x, 42)
69 state.append(x)
70 self.assertEqual(state, [1, 42, 999])
71
72 def test_contextmanager_finally(self):
73 state = []
74 @contextmanager
75 def woohoo():
76 state.append(1)
77 try:
78 yield 42
79 finally:
80 state.append(999)
Florent Xicluna41fe6152010-04-02 18:52:12 +000081 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000082 with woohoo() as x:
83 self.assertEqual(state, [1])
84 self.assertEqual(x, 42)
85 state.append(x)
86 raise ZeroDivisionError()
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000087 self.assertEqual(state, [1, 42, 999])
88
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000089 def test_contextmanager_no_reraise(self):
90 @contextmanager
91 def whee():
92 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +000093 ctx = whee()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000094 ctx.__enter__()
95 # Calling __exit__ should not result in an exception
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000096 self.assertFalse(ctx.__exit__(TypeError, TypeError("foo"), None))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000097
98 def test_contextmanager_trap_yield_after_throw(self):
99 @contextmanager
100 def whoo():
101 try:
102 yield
103 except:
104 yield
Thomas Wouters477c8d52006-05-27 19:21:47 +0000105 ctx = whoo()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000106 ctx.__enter__()
107 self.assertRaises(
108 RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
109 )
110
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000111 def test_contextmanager_except(self):
112 state = []
113 @contextmanager
114 def woohoo():
115 state.append(1)
116 try:
117 yield 42
Guido van Rossumb940e112007-01-10 16:19:56 +0000118 except ZeroDivisionError as e:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000119 state.append(e.args[0])
120 self.assertEqual(state, [1, 42, 999])
121 with woohoo() as x:
122 self.assertEqual(state, [1])
123 self.assertEqual(x, 42)
124 state.append(x)
125 raise ZeroDivisionError(999)
126 self.assertEqual(state, [1, 42, 999])
127
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400128 def test_contextmanager_except_stopiter(self):
129 stop_exc = StopIteration('spam')
130 @contextmanager
131 def woohoo():
132 yield
133 try:
Martin Panter7e3a91a2016-02-10 04:40:48 +0000134 with self.assertWarnsRegex(DeprecationWarning,
Yury Selivanov68333392015-05-22 11:16:47 -0400135 "StopIteration"):
136 with woohoo():
137 raise stop_exc
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400138 except Exception as ex:
139 self.assertIs(ex, stop_exc)
140 else:
141 self.fail('StopIteration was suppressed')
142
143 def test_contextmanager_except_pep479(self):
144 code = """\
145from __future__ import generator_stop
146from contextlib import contextmanager
147@contextmanager
148def woohoo():
149 yield
150"""
151 locals = {}
152 exec(code, locals, locals)
153 woohoo = locals['woohoo']
154
155 stop_exc = StopIteration('spam')
156 try:
157 with woohoo():
158 raise stop_exc
159 except Exception as ex:
160 self.assertIs(ex, stop_exc)
161 else:
162 self.fail('StopIteration was suppressed')
163
svelankar00c75e92017-04-11 05:11:13 -0400164 def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
165 @contextmanager
166 def test_issue29692():
167 try:
168 yield
169 except Exception as exc:
170 raise RuntimeError('issue29692:Chained') from exc
171 try:
172 with test_issue29692():
173 raise ZeroDivisionError
174 except Exception as ex:
175 self.assertIs(type(ex), RuntimeError)
176 self.assertEqual(ex.args[0], 'issue29692:Chained')
177 self.assertIsInstance(ex.__cause__, ZeroDivisionError)
178
179 try:
180 with test_issue29692():
181 raise StopIteration('issue29692:Unchained')
182 except Exception as ex:
183 self.assertIs(type(ex), StopIteration)
184 self.assertEqual(ex.args[0], 'issue29692:Unchained')
185 self.assertIsNone(ex.__cause__)
186
R. David Murray378c0cf2010-02-24 01:46:21 +0000187 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000188 def attribs(**kw):
189 def decorate(func):
190 for k,v in kw.items():
191 setattr(func,k,v)
192 return func
193 return decorate
194 @contextmanager
195 @attribs(foo='bar')
196 def baz(spam):
197 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000198 return baz
199
200 def test_contextmanager_attribs(self):
201 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000202 self.assertEqual(baz.__name__,'baz')
203 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000204
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000205 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000206 def test_contextmanager_doc_attrib(self):
207 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000208 self.assertEqual(baz.__doc__, "Whee!")
209
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000210 @support.requires_docstrings
211 def test_instance_docstring_given_cm_docstring(self):
212 baz = self._create_contextmanager_attribs()(None)
213 self.assertEqual(baz.__doc__, "Whee!")
214
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300215 def test_keywords(self):
216 # Ensure no keyword arguments are inhibited
217 @contextmanager
218 def woohoo(self, func, args, kwds):
219 yield (self, func, args, kwds)
220 with woohoo(self=11, func=22, args=33, kwds=44) as target:
221 self.assertEqual(target, (11, 22, 33, 44))
222
Martin Teichmanndd0e0872018-01-28 05:17:46 +0100223 def test_nokeepref(self):
224 class A:
225 pass
226
227 @contextmanager
228 def woohoo(a, b):
229 a = weakref.ref(a)
230 b = weakref.ref(b)
231 self.assertIsNone(a())
232 self.assertIsNone(b())
233 yield
234
235 with woohoo(A(), b=A()):
236 pass
237
238 def test_param_errors(self):
239 @contextmanager
240 def woohoo(a, *, b):
241 yield
242
243 with self.assertRaises(TypeError):
244 woohoo()
245 with self.assertRaises(TypeError):
246 woohoo(3, 5)
247 with self.assertRaises(TypeError):
248 woohoo(b=3)
249
250 def test_recursive(self):
251 depth = 0
252 @contextmanager
253 def woohoo():
254 nonlocal depth
255 before = depth
256 depth += 1
257 yield
258 depth -= 1
259 self.assertEqual(depth, before)
260
261 @woohoo()
262 def recursive():
263 if depth < 10:
264 recursive()
265
266 recursive()
267 self.assertEqual(depth, 0)
268
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000269
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000270class ClosingTestCase(unittest.TestCase):
271
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000272 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000273 def test_instance_docs(self):
274 # Issue 19330: ensure context manager instances have good docstrings
275 cm_docstring = closing.__doc__
276 obj = closing(None)
277 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000278
279 def test_closing(self):
280 state = []
281 class C:
282 def close(self):
283 state.append(1)
284 x = C()
285 self.assertEqual(state, [])
286 with closing(x) as y:
287 self.assertEqual(x, y)
288 self.assertEqual(state, [1])
289
290 def test_closing_error(self):
291 state = []
292 class C:
293 def close(self):
294 state.append(1)
295 x = C()
296 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000297 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000298 with closing(x) as y:
299 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000300 1 / 0
301 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000302
Jesse-Bakker0784a2e2017-11-23 01:23:28 +0100303
304class NullcontextTestCase(unittest.TestCase):
305 def test_nullcontext(self):
306 class C:
307 pass
308 c = C()
309 with nullcontext(c) as c_in:
310 self.assertIs(c_in, c)
311
312
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000313class FileContextTestCase(unittest.TestCase):
314
315 def testWithOpen(self):
316 tfn = tempfile.mktemp()
317 try:
318 f = None
319 with open(tfn, "w") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000320 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000321 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000322 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000323 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000324 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000325 with open(tfn, "r") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000326 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000327 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000328 1 / 0
329 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000330 finally:
Florent Xicluna41fe6152010-04-02 18:52:12 +0000331 support.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000332
333class LockContextTestCase(unittest.TestCase):
334
335 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000336 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000337 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000338 self.assertTrue(locked())
339 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000340 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000341 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000342 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000343 1 / 0
344 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000345
346 def testWithLock(self):
347 lock = threading.Lock()
348 self.boilerPlate(lock, lock.locked)
349
350 def testWithRLock(self):
351 lock = threading.RLock()
352 self.boilerPlate(lock, lock._is_owned)
353
354 def testWithCondition(self):
355 lock = threading.Condition()
356 def locked():
357 return lock._is_owned()
358 self.boilerPlate(lock, locked)
359
360 def testWithSemaphore(self):
361 lock = threading.Semaphore()
362 def locked():
363 if lock.acquire(False):
364 lock.release()
365 return False
366 else:
367 return True
368 self.boilerPlate(lock, locked)
369
370 def testWithBoundedSemaphore(self):
371 lock = threading.BoundedSemaphore()
372 def locked():
373 if lock.acquire(False):
374 lock.release()
375 return False
376 else:
377 return True
378 self.boilerPlate(lock, locked)
379
Michael Foordb3a89842010-06-30 12:17:50 +0000380
381class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000382 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000383 started = False
384 exc = None
385 catch = False
386
387 def __enter__(self):
388 self.started = True
389 return self
390
391 def __exit__(self, *exc):
392 self.exc = exc
393 return self.catch
394
395
396class TestContextDecorator(unittest.TestCase):
397
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000398 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000399 def test_instance_docs(self):
400 # Issue 19330: ensure context manager instances have good docstrings
401 cm_docstring = mycontext.__doc__
402 obj = mycontext()
403 self.assertEqual(obj.__doc__, cm_docstring)
404
Michael Foordb3a89842010-06-30 12:17:50 +0000405 def test_contextdecorator(self):
406 context = mycontext()
407 with context as result:
408 self.assertIs(result, context)
409 self.assertTrue(context.started)
410
411 self.assertEqual(context.exc, (None, None, None))
412
413
414 def test_contextdecorator_with_exception(self):
415 context = mycontext()
416
Ezio Melottied3a7d22010-12-01 02:32:32 +0000417 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000418 with context:
419 raise NameError('foo')
420 self.assertIsNotNone(context.exc)
421 self.assertIs(context.exc[0], NameError)
422
423 context = mycontext()
424 context.catch = True
425 with context:
426 raise NameError('foo')
427 self.assertIsNotNone(context.exc)
428 self.assertIs(context.exc[0], NameError)
429
430
431 def test_decorator(self):
432 context = mycontext()
433
434 @context
435 def test():
436 self.assertIsNone(context.exc)
437 self.assertTrue(context.started)
438 test()
439 self.assertEqual(context.exc, (None, None, None))
440
441
442 def test_decorator_with_exception(self):
443 context = mycontext()
444
445 @context
446 def test():
447 self.assertIsNone(context.exc)
448 self.assertTrue(context.started)
449 raise NameError('foo')
450
Ezio Melottied3a7d22010-12-01 02:32:32 +0000451 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000452 test()
453 self.assertIsNotNone(context.exc)
454 self.assertIs(context.exc[0], NameError)
455
456
457 def test_decorating_method(self):
458 context = mycontext()
459
460 class Test(object):
461
462 @context
463 def method(self, a, b, c=None):
464 self.a = a
465 self.b = b
466 self.c = c
467
468 # these tests are for argument passing when used as a decorator
469 test = Test()
470 test.method(1, 2)
471 self.assertEqual(test.a, 1)
472 self.assertEqual(test.b, 2)
473 self.assertEqual(test.c, None)
474
475 test = Test()
476 test.method('a', 'b', 'c')
477 self.assertEqual(test.a, 'a')
478 self.assertEqual(test.b, 'b')
479 self.assertEqual(test.c, 'c')
480
481 test = Test()
482 test.method(a=1, b=2)
483 self.assertEqual(test.a, 1)
484 self.assertEqual(test.b, 2)
485
486
487 def test_typo_enter(self):
488 class mycontext(ContextDecorator):
489 def __unter__(self):
490 pass
491 def __exit__(self, *exc):
492 pass
493
494 with self.assertRaises(AttributeError):
495 with mycontext():
496 pass
497
498
499 def test_typo_exit(self):
500 class mycontext(ContextDecorator):
501 def __enter__(self):
502 pass
503 def __uxit__(self, *exc):
504 pass
505
506 with self.assertRaises(AttributeError):
507 with mycontext():
508 pass
509
510
511 def test_contextdecorator_as_mixin(self):
512 class somecontext(object):
513 started = False
514 exc = None
515
516 def __enter__(self):
517 self.started = True
518 return self
519
520 def __exit__(self, *exc):
521 self.exc = exc
522
523 class mycontext(somecontext, ContextDecorator):
524 pass
525
526 context = mycontext()
527 @context
528 def test():
529 self.assertIsNone(context.exc)
530 self.assertTrue(context.started)
531 test()
532 self.assertEqual(context.exc, (None, None, None))
533
534
535 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000536 @contextmanager
537 def woohoo(y):
538 state.append(y)
539 yield
540 state.append(999)
541
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000542 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000543 @woohoo(1)
544 def test(x):
545 self.assertEqual(state, [1])
546 state.append(x)
547 test('something')
548 self.assertEqual(state, [1, 'something', 999])
549
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000550 # Issue #11647: Ensure the decorated function is 'reusable'
551 state = []
552 test('something else')
553 self.assertEqual(state, [1, 'something else', 999])
554
Michael Foordb3a89842010-06-30 12:17:50 +0000555
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800556class TestBaseExitStack:
557 exit_stack = None
Nick Coghlan3267a302012-05-21 22:54:43 +1000558
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000559 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000560 def test_instance_docs(self):
561 # Issue 19330: ensure context manager instances have good docstrings
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800562 cm_docstring = self.exit_stack.__doc__
563 obj = self.exit_stack()
Nick Coghlan059def52013-10-26 18:08:15 +1000564 self.assertEqual(obj.__doc__, cm_docstring)
565
Nick Coghlan3267a302012-05-21 22:54:43 +1000566 def test_no_resources(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800567 with self.exit_stack():
Nick Coghlan3267a302012-05-21 22:54:43 +1000568 pass
569
570 def test_callback(self):
571 expected = [
572 ((), {}),
573 ((1,), {}),
574 ((1,2), {}),
575 ((), dict(example=1)),
576 ((1,), dict(example=1)),
577 ((1,2), dict(example=1)),
578 ]
579 result = []
580 def _exit(*args, **kwds):
581 """Test metadata propagation"""
582 result.append((args, kwds))
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800583 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000584 for args, kwds in reversed(expected):
585 if args and kwds:
586 f = stack.callback(_exit, *args, **kwds)
587 elif args:
588 f = stack.callback(_exit, *args)
589 elif kwds:
590 f = stack.callback(_exit, **kwds)
591 else:
592 f = stack.callback(_exit)
593 self.assertIs(f, _exit)
594 for wrapper in stack._exit_callbacks:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800595 self.assertIs(wrapper[1].__wrapped__, _exit)
596 self.assertNotEqual(wrapper[1].__name__, _exit.__name__)
597 self.assertIsNone(wrapper[1].__doc__, _exit.__doc__)
Nick Coghlan3267a302012-05-21 22:54:43 +1000598 self.assertEqual(result, expected)
599
600 def test_push(self):
601 exc_raised = ZeroDivisionError
602 def _expect_exc(exc_type, exc, exc_tb):
603 self.assertIs(exc_type, exc_raised)
604 def _suppress_exc(*exc_details):
605 return True
606 def _expect_ok(exc_type, exc, exc_tb):
607 self.assertIsNone(exc_type)
608 self.assertIsNone(exc)
609 self.assertIsNone(exc_tb)
610 class ExitCM(object):
611 def __init__(self, check_exc):
612 self.check_exc = check_exc
613 def __enter__(self):
614 self.fail("Should not be called!")
615 def __exit__(self, *exc_details):
616 self.check_exc(*exc_details)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800617 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000618 stack.push(_expect_ok)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800619 self.assertIs(stack._exit_callbacks[-1][1], _expect_ok)
Nick Coghlan3267a302012-05-21 22:54:43 +1000620 cm = ExitCM(_expect_ok)
621 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800622 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000623 stack.push(_suppress_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800624 self.assertIs(stack._exit_callbacks[-1][1], _suppress_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000625 cm = ExitCM(_expect_exc)
626 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800627 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000628 stack.push(_expect_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800629 self.assertIs(stack._exit_callbacks[-1][1], _expect_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000630 stack.push(_expect_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800631 self.assertIs(stack._exit_callbacks[-1][1], _expect_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000632 1/0
633
634 def test_enter_context(self):
635 class TestCM(object):
636 def __enter__(self):
637 result.append(1)
638 def __exit__(self, *exc_details):
639 result.append(3)
640
641 result = []
642 cm = TestCM()
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800643 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000644 @stack.callback # Registered first => cleaned up last
645 def _exit():
646 result.append(4)
647 self.assertIsNotNone(_exit)
648 stack.enter_context(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800649 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000650 result.append(2)
651 self.assertEqual(result, [1, 2, 3, 4])
652
653 def test_close(self):
654 result = []
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800655 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000656 @stack.callback
657 def _exit():
658 result.append(1)
659 self.assertIsNotNone(_exit)
660 stack.close()
661 result.append(2)
662 self.assertEqual(result, [1, 2])
663
664 def test_pop_all(self):
665 result = []
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800666 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000667 @stack.callback
668 def _exit():
669 result.append(3)
670 self.assertIsNotNone(_exit)
671 new_stack = stack.pop_all()
672 result.append(1)
673 result.append(2)
674 new_stack.close()
675 self.assertEqual(result, [1, 2, 3])
676
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000677 def test_exit_raise(self):
678 with self.assertRaises(ZeroDivisionError):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800679 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000680 stack.push(lambda *exc: False)
681 1/0
682
683 def test_exit_suppress(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800684 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000685 stack.push(lambda *exc: True)
686 1/0
687
688 def test_exit_exception_chaining_reference(self):
689 # Sanity check to make sure that ExitStack chaining matches
690 # actual nested with statements
691 class RaiseExc:
692 def __init__(self, exc):
693 self.exc = exc
694 def __enter__(self):
695 return self
696 def __exit__(self, *exc_details):
697 raise self.exc
698
Nick Coghlan77452fc2012-06-01 22:48:32 +1000699 class RaiseExcWithContext:
700 def __init__(self, outer, inner):
701 self.outer = outer
702 self.inner = inner
703 def __enter__(self):
704 return self
705 def __exit__(self, *exc_details):
706 try:
707 raise self.inner
708 except:
709 raise self.outer
710
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000711 class SuppressExc:
712 def __enter__(self):
713 return self
714 def __exit__(self, *exc_details):
715 type(self).saved_details = exc_details
716 return True
717
718 try:
719 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000720 with RaiseExcWithContext(KeyError, AttributeError):
721 with SuppressExc():
722 with RaiseExc(ValueError):
723 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000724 except IndexError as exc:
725 self.assertIsInstance(exc.__context__, KeyError)
726 self.assertIsInstance(exc.__context__.__context__, AttributeError)
727 # Inner exceptions were suppressed
728 self.assertIsNone(exc.__context__.__context__.__context__)
729 else:
730 self.fail("Expected IndexError, but no exception was raised")
731 # Check the inner exceptions
732 inner_exc = SuppressExc.saved_details[1]
733 self.assertIsInstance(inner_exc, ValueError)
734 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
735
736 def test_exit_exception_chaining(self):
737 # Ensure exception chaining matches the reference behaviour
738 def raise_exc(exc):
739 raise exc
740
741 saved_details = None
742 def suppress_exc(*exc_details):
743 nonlocal saved_details
744 saved_details = exc_details
745 return True
746
747 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800748 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000749 stack.callback(raise_exc, IndexError)
750 stack.callback(raise_exc, KeyError)
751 stack.callback(raise_exc, AttributeError)
752 stack.push(suppress_exc)
753 stack.callback(raise_exc, ValueError)
754 1 / 0
755 except IndexError as exc:
756 self.assertIsInstance(exc.__context__, KeyError)
757 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000758 # Inner exceptions were suppressed
759 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000760 else:
761 self.fail("Expected IndexError, but no exception was raised")
762 # Check the inner exceptions
763 inner_exc = saved_details[1]
764 self.assertIsInstance(inner_exc, ValueError)
765 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
766
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000767 def test_exit_exception_non_suppressing(self):
768 # http://bugs.python.org/issue19092
769 def raise_exc(exc):
770 raise exc
771
772 def suppress_exc(*exc_details):
773 return True
774
775 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800776 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000777 stack.callback(lambda: None)
778 stack.callback(raise_exc, IndexError)
779 except Exception as exc:
780 self.assertIsInstance(exc, IndexError)
781 else:
782 self.fail("Expected IndexError, but no exception was raised")
783
784 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800785 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000786 stack.callback(raise_exc, KeyError)
787 stack.push(suppress_exc)
788 stack.callback(raise_exc, IndexError)
789 except Exception as exc:
790 self.assertIsInstance(exc, KeyError)
791 else:
792 self.fail("Expected KeyError, but no exception was raised")
793
Nick Coghlan09761e72014-01-22 22:24:46 +1000794 def test_exit_exception_with_correct_context(self):
795 # http://bugs.python.org/issue20317
796 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000797 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000798 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000799 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000800 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000801 raise exc
802
803 exc1 = Exception(1)
804 exc2 = Exception(2)
805 exc3 = Exception(3)
806 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000807
808 # The contextmanager already fixes the context, so prior to the
809 # fix, ExitStack would try to fix it *again* and get into an
810 # infinite self-referential loop
811 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800812 with self.exit_stack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000813 stack.enter_context(gets_the_context_right(exc4))
814 stack.enter_context(gets_the_context_right(exc3))
815 stack.enter_context(gets_the_context_right(exc2))
816 raise exc1
817 except Exception as exc:
818 self.assertIs(exc, exc4)
819 self.assertIs(exc.__context__, exc3)
820 self.assertIs(exc.__context__.__context__, exc2)
821 self.assertIs(exc.__context__.__context__.__context__, exc1)
822 self.assertIsNone(
823 exc.__context__.__context__.__context__.__context__)
824
825 def test_exit_exception_with_existing_context(self):
826 # Addresses a lack of test coverage discovered after checking in a
827 # fix for issue 20317 that still contained debugging code.
828 def raise_nested(inner_exc, outer_exc):
829 try:
830 raise inner_exc
831 finally:
832 raise outer_exc
833 exc1 = Exception(1)
834 exc2 = Exception(2)
835 exc3 = Exception(3)
836 exc4 = Exception(4)
837 exc5 = Exception(5)
838 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800839 with self.exit_stack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000840 stack.callback(raise_nested, exc4, exc5)
841 stack.callback(raise_nested, exc2, exc3)
842 raise exc1
843 except Exception as exc:
844 self.assertIs(exc, exc5)
845 self.assertIs(exc.__context__, exc4)
846 self.assertIs(exc.__context__.__context__, exc3)
847 self.assertIs(exc.__context__.__context__.__context__, exc2)
848 self.assertIs(
849 exc.__context__.__context__.__context__.__context__, exc1)
850 self.assertIsNone(
851 exc.__context__.__context__.__context__.__context__.__context__)
852
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000853 def test_body_exception_suppress(self):
854 def suppress_exc(*exc_details):
855 return True
856 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800857 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000858 stack.push(suppress_exc)
859 1/0
860 except IndexError as exc:
861 self.fail("Expected no exception, got IndexError")
862
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000863 def test_exit_exception_chaining_suppress(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800864 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000865 stack.push(lambda *exc: True)
866 stack.push(lambda *exc: 1/0)
867 stack.push(lambda *exc: {}[1])
868
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000869 def test_excessive_nesting(self):
870 # The original implementation would die with RecursionError here
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800871 with self.exit_stack() as stack:
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000872 for i in range(10000):
873 stack.callback(int)
874
Nick Coghlan3267a302012-05-21 22:54:43 +1000875 def test_instance_bypass(self):
876 class Example(object): pass
877 cm = Example()
878 cm.__exit__ = object()
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800879 stack = self.exit_stack()
Nick Coghlan3267a302012-05-21 22:54:43 +1000880 self.assertRaises(AttributeError, stack.enter_context, cm)
881 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800882 self.assertIs(stack._exit_callbacks[-1][1], cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000883
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700884 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300885 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700886 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300887 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700888
889 @contextmanager
890 def second():
891 try:
892 yield 1
893 except Exception as exc:
894 raise UniqueException("new exception") from exc
895
896 @contextmanager
897 def first():
898 try:
899 yield 1
900 except Exception as exc:
901 raise exc
902
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300903 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700904 # handler which chain raised a new UniqueException.
905 with self.assertRaises(UniqueException) as err_ctx:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800906 with self.exit_stack() as es_ctx:
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700907 es_ctx.enter_context(second())
908 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300909 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700910
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300911 exc = err_ctx.exception
912 self.assertIsInstance(exc, UniqueException)
913 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
914 self.assertIsNone(exc.__context__.__context__)
915 self.assertIsNone(exc.__context__.__cause__)
916 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700917
Berker Peksagbb44fe02014-11-28 23:28:06 +0200918
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800919class TestExitStack(TestBaseExitStack, unittest.TestCase):
920 exit_stack = ExitStack
921
922
Berker Peksagbb44fe02014-11-28 23:28:06 +0200923class TestRedirectStream:
924
925 redirect_stream = None
926 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700927
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000928 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000929 def test_instance_docs(self):
930 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200931 cm_docstring = self.redirect_stream.__doc__
932 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000933 self.assertEqual(obj.__doc__, cm_docstring)
934
Nick Coghlan8e113b42013-11-03 17:00:51 +1000935 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200936 orig_stdout = getattr(sys, self.orig_stream)
937 self.redirect_stream(None)
938 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000939
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700940 def test_redirect_to_string_io(self):
941 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000942 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200943 orig_stdout = getattr(sys, self.orig_stream)
944 with self.redirect_stream(f):
945 print(msg, file=getattr(sys, self.orig_stream))
946 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000947 s = f.getvalue().strip()
948 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000949
Nick Coghlan8608d262013-10-20 00:30:51 +1000950 def test_enter_result_is_target(self):
951 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200952 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000953 self.assertIs(enter_result, f)
954
955 def test_cm_is_reusable(self):
956 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200957 write_to_f = self.redirect_stream(f)
958 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8608d262013-10-20 00:30:51 +1000959 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200960 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000961 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200962 print("World!", file=getattr(sys, self.orig_stream))
963 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000964 s = f.getvalue()
965 self.assertEqual(s, "Hello World!\n")
966
Nick Coghlan8e113b42013-11-03 17:00:51 +1000967 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000968 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200969 write_to_f = self.redirect_stream(f)
970 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000971 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200972 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000973 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200974 print("World!", file=getattr(sys, self.orig_stream))
975 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000976 s = f.getvalue()
977 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000978
979
Berker Peksagbb44fe02014-11-28 23:28:06 +0200980class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
981
982 redirect_stream = redirect_stdout
983 orig_stream = "stdout"
984
985
986class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
987
988 redirect_stream = redirect_stderr
989 orig_stream = "stderr"
990
991
Nick Coghlan240f86d2013-10-17 23:40:57 +1000992class TestSuppress(unittest.TestCase):
993
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000994 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000995 def test_instance_docs(self):
996 # Issue 19330: ensure context manager instances have good docstrings
997 cm_docstring = suppress.__doc__
998 obj = suppress()
999 self.assertEqual(obj.__doc__, cm_docstring)
1000
Nick Coghlan8608d262013-10-20 00:30:51 +10001001 def test_no_result_from_enter(self):
1002 with suppress(ValueError) as enter_result:
1003 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +10001004
Nick Coghlan8608d262013-10-20 00:30:51 +10001005 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +10001006 with suppress(ValueError):
1007 self.assertEqual(pow(2, 5), 32)
1008
1009 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +10001010 with suppress(TypeError):
1011 len(5)
1012
Nick Coghlan059def52013-10-26 18:08:15 +10001013 def test_exception_hierarchy(self):
1014 with suppress(LookupError):
1015 'Hello'[50]
1016
1017 def test_other_exception(self):
1018 with self.assertRaises(ZeroDivisionError):
1019 with suppress(TypeError):
1020 1/0
1021
1022 def test_no_args(self):
1023 with self.assertRaises(ZeroDivisionError):
1024 with suppress():
1025 1/0
1026
Nick Coghlan240f86d2013-10-17 23:40:57 +10001027 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +10001028 with suppress(ZeroDivisionError, TypeError):
1029 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +10001030 with suppress(ZeroDivisionError, TypeError):
1031 len(5)
1032
Nick Coghlan8608d262013-10-20 00:30:51 +10001033 def test_cm_is_reentrant(self):
1034 ignore_exceptions = suppress(Exception)
1035 with ignore_exceptions:
1036 pass
1037 with ignore_exceptions:
1038 len(5)
1039 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +10001040 with ignore_exceptions: # Check nested usage
1041 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +00001042 outer_continued = True
1043 1/0
1044 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +10001045
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001046if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -04001047 unittest.main()