blob: e6500da0ba54a6d531a8a9e2200465477eb07f38 [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
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02006import threading
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00007import unittest
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00008from contextlib import * # Tests __all__
Benjamin Petersonee8712c2008-05-20 21:35:26 +00009from test import support
Hai Shi96a6a6d2020-07-09 21:25:10 +080010from test.support import os_helper
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):
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400129 @contextmanager
130 def woohoo():
131 yield
Miss Islington (bot)68b46902021-07-20 12:12:47 -0700132
133 class StopIterationSubclass(StopIteration):
134 pass
135
136 for stop_exc in (StopIteration('spam'), StopIterationSubclass('spam')):
137 with self.subTest(type=type(stop_exc)):
138 try:
139 with woohoo():
140 raise stop_exc
141 except Exception as ex:
142 self.assertIs(ex, stop_exc)
143 else:
144 self.fail(f'{stop_exc} was suppressed')
Yury Selivanov8170e8c2015-05-09 11:44:30 -0400145
146 def test_contextmanager_except_pep479(self):
147 code = """\
148from __future__ import generator_stop
149from contextlib import contextmanager
150@contextmanager
151def woohoo():
152 yield
153"""
154 locals = {}
155 exec(code, locals, locals)
156 woohoo = locals['woohoo']
157
158 stop_exc = StopIteration('spam')
159 try:
160 with woohoo():
161 raise stop_exc
162 except Exception as ex:
163 self.assertIs(ex, stop_exc)
164 else:
165 self.fail('StopIteration was suppressed')
166
svelankar00c75e92017-04-11 05:11:13 -0400167 def test_contextmanager_do_not_unchain_non_stopiteration_exceptions(self):
168 @contextmanager
169 def test_issue29692():
170 try:
171 yield
172 except Exception as exc:
173 raise RuntimeError('issue29692:Chained') from exc
174 try:
175 with test_issue29692():
176 raise ZeroDivisionError
177 except Exception as ex:
178 self.assertIs(type(ex), RuntimeError)
179 self.assertEqual(ex.args[0], 'issue29692:Chained')
180 self.assertIsInstance(ex.__cause__, ZeroDivisionError)
181
182 try:
183 with test_issue29692():
184 raise StopIteration('issue29692:Unchained')
185 except Exception as ex:
186 self.assertIs(type(ex), StopIteration)
187 self.assertEqual(ex.args[0], 'issue29692:Unchained')
188 self.assertIsNone(ex.__cause__)
189
R. David Murray378c0cf2010-02-24 01:46:21 +0000190 def _create_contextmanager_attribs(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000191 def attribs(**kw):
192 def decorate(func):
193 for k,v in kw.items():
194 setattr(func,k,v)
195 return func
196 return decorate
197 @contextmanager
198 @attribs(foo='bar')
199 def baz(spam):
200 """Whee!"""
R. David Murray378c0cf2010-02-24 01:46:21 +0000201 return baz
202
203 def test_contextmanager_attribs(self):
204 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000205 self.assertEqual(baz.__name__,'baz')
206 self.assertEqual(baz.foo, 'bar')
R. David Murray378c0cf2010-02-24 01:46:21 +0000207
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000208 @support.requires_docstrings
R. David Murray378c0cf2010-02-24 01:46:21 +0000209 def test_contextmanager_doc_attrib(self):
210 baz = self._create_contextmanager_attribs()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000211 self.assertEqual(baz.__doc__, "Whee!")
212
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000213 @support.requires_docstrings
214 def test_instance_docstring_given_cm_docstring(self):
215 baz = self._create_contextmanager_attribs()(None)
216 self.assertEqual(baz.__doc__, "Whee!")
217
Serhiy Storchaka101ff352015-06-28 17:06:07 +0300218 def test_keywords(self):
219 # Ensure no keyword arguments are inhibited
220 @contextmanager
221 def woohoo(self, func, args, kwds):
222 yield (self, func, args, kwds)
223 with woohoo(self=11, func=22, args=33, kwds=44) as target:
224 self.assertEqual(target, (11, 22, 33, 44))
225
Martin Teichmanndd0e0872018-01-28 05:17:46 +0100226 def test_nokeepref(self):
227 class A:
228 pass
229
230 @contextmanager
231 def woohoo(a, b):
232 a = weakref.ref(a)
233 b = weakref.ref(b)
234 self.assertIsNone(a())
235 self.assertIsNone(b())
236 yield
237
238 with woohoo(A(), b=A()):
239 pass
240
241 def test_param_errors(self):
242 @contextmanager
243 def woohoo(a, *, b):
244 yield
245
246 with self.assertRaises(TypeError):
247 woohoo()
248 with self.assertRaises(TypeError):
249 woohoo(3, 5)
250 with self.assertRaises(TypeError):
251 woohoo(b=3)
252
253 def test_recursive(self):
254 depth = 0
255 @contextmanager
256 def woohoo():
257 nonlocal depth
258 before = depth
259 depth += 1
260 yield
261 depth -= 1
262 self.assertEqual(depth, before)
263
264 @woohoo()
265 def recursive():
266 if depth < 10:
267 recursive()
268
269 recursive()
270 self.assertEqual(depth, 0)
271
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000272
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000273class ClosingTestCase(unittest.TestCase):
274
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000275 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000276 def test_instance_docs(self):
277 # Issue 19330: ensure context manager instances have good docstrings
278 cm_docstring = closing.__doc__
279 obj = closing(None)
280 self.assertEqual(obj.__doc__, cm_docstring)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000281
282 def test_closing(self):
283 state = []
284 class C:
285 def close(self):
286 state.append(1)
287 x = C()
288 self.assertEqual(state, [])
289 with closing(x) as y:
290 self.assertEqual(x, y)
291 self.assertEqual(state, [1])
292
293 def test_closing_error(self):
294 state = []
295 class C:
296 def close(self):
297 state.append(1)
298 x = C()
299 self.assertEqual(state, [])
Florent Xicluna41fe6152010-04-02 18:52:12 +0000300 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000301 with closing(x) as y:
302 self.assertEqual(x, y)
Florent Xicluna41fe6152010-04-02 18:52:12 +0000303 1 / 0
304 self.assertEqual(state, [1])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000305
Jesse-Bakker0784a2e2017-11-23 01:23:28 +0100306
307class NullcontextTestCase(unittest.TestCase):
308 def test_nullcontext(self):
309 class C:
310 pass
311 c = C()
312 with nullcontext(c) as c_in:
313 self.assertIs(c_in, c)
314
315
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000316class FileContextTestCase(unittest.TestCase):
317
318 def testWithOpen(self):
319 tfn = tempfile.mktemp()
320 try:
321 f = None
Inada Naoki35715d12021-04-04 09:01:23 +0900322 with open(tfn, "w", encoding="utf-8") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000323 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000324 f.write("Booh\n")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000325 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000326 f = None
Florent Xicluna41fe6152010-04-02 18:52:12 +0000327 with self.assertRaises(ZeroDivisionError):
Inada Naoki35715d12021-04-04 09:01:23 +0900328 with open(tfn, "r", encoding="utf-8") as f:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000329 self.assertFalse(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000330 self.assertEqual(f.read(), "Booh\n")
Florent Xicluna41fe6152010-04-02 18:52:12 +0000331 1 / 0
332 self.assertTrue(f.closed)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000333 finally:
Hai Shi96a6a6d2020-07-09 21:25:10 +0800334 os_helper.unlink(tfn)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000335
336class LockContextTestCase(unittest.TestCase):
337
338 def boilerPlate(self, lock, locked):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000339 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000340 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000341 self.assertTrue(locked())
342 self.assertFalse(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000343 with self.assertRaises(ZeroDivisionError):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000344 with lock:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000345 self.assertTrue(locked())
Florent Xicluna41fe6152010-04-02 18:52:12 +0000346 1 / 0
347 self.assertFalse(locked())
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000348
349 def testWithLock(self):
350 lock = threading.Lock()
351 self.boilerPlate(lock, lock.locked)
352
353 def testWithRLock(self):
354 lock = threading.RLock()
355 self.boilerPlate(lock, lock._is_owned)
356
357 def testWithCondition(self):
358 lock = threading.Condition()
359 def locked():
360 return lock._is_owned()
361 self.boilerPlate(lock, locked)
362
363 def testWithSemaphore(self):
364 lock = threading.Semaphore()
365 def locked():
366 if lock.acquire(False):
367 lock.release()
368 return False
369 else:
370 return True
371 self.boilerPlate(lock, locked)
372
373 def testWithBoundedSemaphore(self):
374 lock = threading.BoundedSemaphore()
375 def locked():
376 if lock.acquire(False):
377 lock.release()
378 return False
379 else:
380 return True
381 self.boilerPlate(lock, locked)
382
Michael Foordb3a89842010-06-30 12:17:50 +0000383
384class mycontext(ContextDecorator):
Nick Coghlan059def52013-10-26 18:08:15 +1000385 """Example decoration-compatible context manager for testing"""
Michael Foordb3a89842010-06-30 12:17:50 +0000386 started = False
387 exc = None
388 catch = False
389
390 def __enter__(self):
391 self.started = True
392 return self
393
394 def __exit__(self, *exc):
395 self.exc = exc
396 return self.catch
397
398
399class TestContextDecorator(unittest.TestCase):
400
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000401 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000402 def test_instance_docs(self):
403 # Issue 19330: ensure context manager instances have good docstrings
404 cm_docstring = mycontext.__doc__
405 obj = mycontext()
406 self.assertEqual(obj.__doc__, cm_docstring)
407
Michael Foordb3a89842010-06-30 12:17:50 +0000408 def test_contextdecorator(self):
409 context = mycontext()
410 with context as result:
411 self.assertIs(result, context)
412 self.assertTrue(context.started)
413
414 self.assertEqual(context.exc, (None, None, None))
415
416
417 def test_contextdecorator_with_exception(self):
418 context = mycontext()
419
Ezio Melottied3a7d22010-12-01 02:32:32 +0000420 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000421 with context:
422 raise NameError('foo')
423 self.assertIsNotNone(context.exc)
424 self.assertIs(context.exc[0], NameError)
425
426 context = mycontext()
427 context.catch = True
428 with context:
429 raise NameError('foo')
430 self.assertIsNotNone(context.exc)
431 self.assertIs(context.exc[0], NameError)
432
433
434 def test_decorator(self):
435 context = mycontext()
436
437 @context
438 def test():
439 self.assertIsNone(context.exc)
440 self.assertTrue(context.started)
441 test()
442 self.assertEqual(context.exc, (None, None, None))
443
444
445 def test_decorator_with_exception(self):
446 context = mycontext()
447
448 @context
449 def test():
450 self.assertIsNone(context.exc)
451 self.assertTrue(context.started)
452 raise NameError('foo')
453
Ezio Melottied3a7d22010-12-01 02:32:32 +0000454 with self.assertRaisesRegex(NameError, 'foo'):
Michael Foordb3a89842010-06-30 12:17:50 +0000455 test()
456 self.assertIsNotNone(context.exc)
457 self.assertIs(context.exc[0], NameError)
458
459
460 def test_decorating_method(self):
461 context = mycontext()
462
463 class Test(object):
464
465 @context
466 def method(self, a, b, c=None):
467 self.a = a
468 self.b = b
469 self.c = c
470
471 # these tests are for argument passing when used as a decorator
472 test = Test()
473 test.method(1, 2)
474 self.assertEqual(test.a, 1)
475 self.assertEqual(test.b, 2)
476 self.assertEqual(test.c, None)
477
478 test = Test()
479 test.method('a', 'b', 'c')
480 self.assertEqual(test.a, 'a')
481 self.assertEqual(test.b, 'b')
482 self.assertEqual(test.c, 'c')
483
484 test = Test()
485 test.method(a=1, b=2)
486 self.assertEqual(test.a, 1)
487 self.assertEqual(test.b, 2)
488
489
490 def test_typo_enter(self):
491 class mycontext(ContextDecorator):
492 def __unter__(self):
493 pass
494 def __exit__(self, *exc):
495 pass
496
497 with self.assertRaises(AttributeError):
498 with mycontext():
499 pass
500
501
502 def test_typo_exit(self):
503 class mycontext(ContextDecorator):
504 def __enter__(self):
505 pass
506 def __uxit__(self, *exc):
507 pass
508
509 with self.assertRaises(AttributeError):
510 with mycontext():
511 pass
512
513
514 def test_contextdecorator_as_mixin(self):
515 class somecontext(object):
516 started = False
517 exc = None
518
519 def __enter__(self):
520 self.started = True
521 return self
522
523 def __exit__(self, *exc):
524 self.exc = exc
525
526 class mycontext(somecontext, ContextDecorator):
527 pass
528
529 context = mycontext()
530 @context
531 def test():
532 self.assertIsNone(context.exc)
533 self.assertTrue(context.started)
534 test()
535 self.assertEqual(context.exc, (None, None, None))
536
537
538 def test_contextmanager_as_decorator(self):
Michael Foordb3a89842010-06-30 12:17:50 +0000539 @contextmanager
540 def woohoo(y):
541 state.append(y)
542 yield
543 state.append(999)
544
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000545 state = []
Michael Foordb3a89842010-06-30 12:17:50 +0000546 @woohoo(1)
547 def test(x):
548 self.assertEqual(state, [1])
549 state.append(x)
550 test('something')
551 self.assertEqual(state, [1, 'something', 999])
552
Nick Coghlan0ded3e32011-05-05 23:49:25 +1000553 # Issue #11647: Ensure the decorated function is 'reusable'
554 state = []
555 test('something else')
556 self.assertEqual(state, [1, 'something else', 999])
557
Michael Foordb3a89842010-06-30 12:17:50 +0000558
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800559class TestBaseExitStack:
560 exit_stack = None
Nick Coghlan3267a302012-05-21 22:54:43 +1000561
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000562 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000563 def test_instance_docs(self):
564 # Issue 19330: ensure context manager instances have good docstrings
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800565 cm_docstring = self.exit_stack.__doc__
566 obj = self.exit_stack()
Nick Coghlan059def52013-10-26 18:08:15 +1000567 self.assertEqual(obj.__doc__, cm_docstring)
568
Nick Coghlan3267a302012-05-21 22:54:43 +1000569 def test_no_resources(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800570 with self.exit_stack():
Nick Coghlan3267a302012-05-21 22:54:43 +1000571 pass
572
573 def test_callback(self):
574 expected = [
575 ((), {}),
576 ((1,), {}),
577 ((1,2), {}),
578 ((), dict(example=1)),
579 ((1,), dict(example=1)),
580 ((1,2), dict(example=1)),
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300581 ((1,2), dict(self=3, callback=4)),
Nick Coghlan3267a302012-05-21 22:54:43 +1000582 ]
583 result = []
584 def _exit(*args, **kwds):
585 """Test metadata propagation"""
586 result.append((args, kwds))
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800587 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000588 for args, kwds in reversed(expected):
589 if args and kwds:
590 f = stack.callback(_exit, *args, **kwds)
591 elif args:
592 f = stack.callback(_exit, *args)
593 elif kwds:
594 f = stack.callback(_exit, **kwds)
595 else:
596 f = stack.callback(_exit)
597 self.assertIs(f, _exit)
598 for wrapper in stack._exit_callbacks:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800599 self.assertIs(wrapper[1].__wrapped__, _exit)
600 self.assertNotEqual(wrapper[1].__name__, _exit.__name__)
601 self.assertIsNone(wrapper[1].__doc__, _exit.__doc__)
Nick Coghlan3267a302012-05-21 22:54:43 +1000602 self.assertEqual(result, expected)
603
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300604 result = []
605 with self.exit_stack() as stack:
606 with self.assertRaises(TypeError):
607 stack.callback(arg=1)
608 with self.assertRaises(TypeError):
609 self.exit_stack.callback(arg=2)
Serhiy Storchaka142566c2019-06-05 18:22:31 +0300610 with self.assertRaises(TypeError):
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300611 stack.callback(callback=_exit, arg=3)
Serhiy Storchaka142566c2019-06-05 18:22:31 +0300612 self.assertEqual(result, [])
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300613
Nick Coghlan3267a302012-05-21 22:54:43 +1000614 def test_push(self):
615 exc_raised = ZeroDivisionError
616 def _expect_exc(exc_type, exc, exc_tb):
617 self.assertIs(exc_type, exc_raised)
618 def _suppress_exc(*exc_details):
619 return True
620 def _expect_ok(exc_type, exc, exc_tb):
621 self.assertIsNone(exc_type)
622 self.assertIsNone(exc)
623 self.assertIsNone(exc_tb)
624 class ExitCM(object):
625 def __init__(self, check_exc):
626 self.check_exc = check_exc
627 def __enter__(self):
628 self.fail("Should not be called!")
629 def __exit__(self, *exc_details):
630 self.check_exc(*exc_details)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800631 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000632 stack.push(_expect_ok)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800633 self.assertIs(stack._exit_callbacks[-1][1], _expect_ok)
Nick Coghlan3267a302012-05-21 22:54:43 +1000634 cm = ExitCM(_expect_ok)
635 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800636 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000637 stack.push(_suppress_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800638 self.assertIs(stack._exit_callbacks[-1][1], _suppress_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000639 cm = ExitCM(_expect_exc)
640 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800641 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000642 stack.push(_expect_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800643 self.assertIs(stack._exit_callbacks[-1][1], _expect_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000644 stack.push(_expect_exc)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800645 self.assertIs(stack._exit_callbacks[-1][1], _expect_exc)
Nick Coghlan3267a302012-05-21 22:54:43 +1000646 1/0
647
648 def test_enter_context(self):
649 class TestCM(object):
650 def __enter__(self):
651 result.append(1)
652 def __exit__(self, *exc_details):
653 result.append(3)
654
655 result = []
656 cm = TestCM()
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800657 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000658 @stack.callback # Registered first => cleaned up last
659 def _exit():
660 result.append(4)
661 self.assertIsNotNone(_exit)
662 stack.enter_context(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800663 self.assertIs(stack._exit_callbacks[-1][1].__self__, cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000664 result.append(2)
665 self.assertEqual(result, [1, 2, 3, 4])
666
667 def test_close(self):
668 result = []
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800669 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000670 @stack.callback
671 def _exit():
672 result.append(1)
673 self.assertIsNotNone(_exit)
674 stack.close()
675 result.append(2)
676 self.assertEqual(result, [1, 2])
677
678 def test_pop_all(self):
679 result = []
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800680 with self.exit_stack() as stack:
Nick Coghlan3267a302012-05-21 22:54:43 +1000681 @stack.callback
682 def _exit():
683 result.append(3)
684 self.assertIsNotNone(_exit)
685 new_stack = stack.pop_all()
686 result.append(1)
687 result.append(2)
688 new_stack.close()
689 self.assertEqual(result, [1, 2, 3])
690
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000691 def test_exit_raise(self):
692 with self.assertRaises(ZeroDivisionError):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800693 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000694 stack.push(lambda *exc: False)
695 1/0
696
697 def test_exit_suppress(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800698 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000699 stack.push(lambda *exc: True)
700 1/0
701
702 def test_exit_exception_chaining_reference(self):
703 # Sanity check to make sure that ExitStack chaining matches
704 # actual nested with statements
705 class RaiseExc:
706 def __init__(self, exc):
707 self.exc = exc
708 def __enter__(self):
709 return self
710 def __exit__(self, *exc_details):
711 raise self.exc
712
Nick Coghlan77452fc2012-06-01 22:48:32 +1000713 class RaiseExcWithContext:
714 def __init__(self, outer, inner):
715 self.outer = outer
716 self.inner = inner
717 def __enter__(self):
718 return self
719 def __exit__(self, *exc_details):
720 try:
721 raise self.inner
722 except:
723 raise self.outer
724
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000725 class SuppressExc:
726 def __enter__(self):
727 return self
728 def __exit__(self, *exc_details):
729 type(self).saved_details = exc_details
730 return True
731
732 try:
733 with RaiseExc(IndexError):
Nick Coghlan77452fc2012-06-01 22:48:32 +1000734 with RaiseExcWithContext(KeyError, AttributeError):
735 with SuppressExc():
736 with RaiseExc(ValueError):
737 1 / 0
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000738 except IndexError as exc:
739 self.assertIsInstance(exc.__context__, KeyError)
740 self.assertIsInstance(exc.__context__.__context__, AttributeError)
741 # Inner exceptions were suppressed
742 self.assertIsNone(exc.__context__.__context__.__context__)
743 else:
744 self.fail("Expected IndexError, but no exception was raised")
745 # Check the inner exceptions
746 inner_exc = SuppressExc.saved_details[1]
747 self.assertIsInstance(inner_exc, ValueError)
748 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
749
750 def test_exit_exception_chaining(self):
751 # Ensure exception chaining matches the reference behaviour
752 def raise_exc(exc):
753 raise exc
754
755 saved_details = None
756 def suppress_exc(*exc_details):
757 nonlocal saved_details
758 saved_details = exc_details
759 return True
760
761 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800762 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000763 stack.callback(raise_exc, IndexError)
764 stack.callback(raise_exc, KeyError)
765 stack.callback(raise_exc, AttributeError)
766 stack.push(suppress_exc)
767 stack.callback(raise_exc, ValueError)
768 1 / 0
769 except IndexError as exc:
770 self.assertIsInstance(exc.__context__, KeyError)
771 self.assertIsInstance(exc.__context__.__context__, AttributeError)
Nick Coghlan77452fc2012-06-01 22:48:32 +1000772 # Inner exceptions were suppressed
773 self.assertIsNone(exc.__context__.__context__.__context__)
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000774 else:
775 self.fail("Expected IndexError, but no exception was raised")
776 # Check the inner exceptions
777 inner_exc = saved_details[1]
778 self.assertIsInstance(inner_exc, ValueError)
779 self.assertIsInstance(inner_exc.__context__, ZeroDivisionError)
780
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000781 def test_exit_exception_non_suppressing(self):
782 # http://bugs.python.org/issue19092
783 def raise_exc(exc):
784 raise exc
785
786 def suppress_exc(*exc_details):
787 return True
788
789 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800790 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000791 stack.callback(lambda: None)
792 stack.callback(raise_exc, IndexError)
793 except Exception as exc:
794 self.assertIsInstance(exc, IndexError)
795 else:
796 self.fail("Expected IndexError, but no exception was raised")
797
798 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800799 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000800 stack.callback(raise_exc, KeyError)
801 stack.push(suppress_exc)
802 stack.callback(raise_exc, IndexError)
803 except Exception as exc:
804 self.assertIsInstance(exc, KeyError)
805 else:
806 self.fail("Expected KeyError, but no exception was raised")
807
Nick Coghlan09761e72014-01-22 22:24:46 +1000808 def test_exit_exception_with_correct_context(self):
809 # http://bugs.python.org/issue20317
810 @contextmanager
Nick Coghlanadd94c92014-01-24 23:05:45 +1000811 def gets_the_context_right(exc):
Nick Coghlan09761e72014-01-22 22:24:46 +1000812 try:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000813 yield
Nick Coghlan09761e72014-01-22 22:24:46 +1000814 finally:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000815 raise exc
816
817 exc1 = Exception(1)
818 exc2 = Exception(2)
819 exc3 = Exception(3)
820 exc4 = Exception(4)
Nick Coghlan09761e72014-01-22 22:24:46 +1000821
822 # The contextmanager already fixes the context, so prior to the
823 # fix, ExitStack would try to fix it *again* and get into an
824 # infinite self-referential loop
825 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800826 with self.exit_stack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000827 stack.enter_context(gets_the_context_right(exc4))
828 stack.enter_context(gets_the_context_right(exc3))
829 stack.enter_context(gets_the_context_right(exc2))
830 raise exc1
831 except Exception as exc:
832 self.assertIs(exc, exc4)
833 self.assertIs(exc.__context__, exc3)
834 self.assertIs(exc.__context__.__context__, exc2)
835 self.assertIs(exc.__context__.__context__.__context__, exc1)
836 self.assertIsNone(
837 exc.__context__.__context__.__context__.__context__)
838
839 def test_exit_exception_with_existing_context(self):
840 # Addresses a lack of test coverage discovered after checking in a
841 # fix for issue 20317 that still contained debugging code.
842 def raise_nested(inner_exc, outer_exc):
843 try:
844 raise inner_exc
845 finally:
846 raise outer_exc
847 exc1 = Exception(1)
848 exc2 = Exception(2)
849 exc3 = Exception(3)
850 exc4 = Exception(4)
851 exc5 = Exception(5)
852 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800853 with self.exit_stack() as stack:
Nick Coghlanadd94c92014-01-24 23:05:45 +1000854 stack.callback(raise_nested, exc4, exc5)
855 stack.callback(raise_nested, exc2, exc3)
856 raise exc1
857 except Exception as exc:
858 self.assertIs(exc, exc5)
859 self.assertIs(exc.__context__, exc4)
860 self.assertIs(exc.__context__.__context__, exc3)
861 self.assertIs(exc.__context__.__context__.__context__, exc2)
862 self.assertIs(
863 exc.__context__.__context__.__context__.__context__, exc1)
864 self.assertIsNone(
865 exc.__context__.__context__.__context__.__context__.__context__)
866
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000867 def test_body_exception_suppress(self):
868 def suppress_exc(*exc_details):
869 return True
870 try:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800871 with self.exit_stack() as stack:
Nick Coghlan1a33b2f2013-10-01 23:24:56 +1000872 stack.push(suppress_exc)
873 1/0
874 except IndexError as exc:
875 self.fail("Expected no exception, got IndexError")
876
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000877 def test_exit_exception_chaining_suppress(self):
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800878 with self.exit_stack() as stack:
Nick Coghlanc73e8c22012-05-31 23:49:26 +1000879 stack.push(lambda *exc: True)
880 stack.push(lambda *exc: 1/0)
881 stack.push(lambda *exc: {}[1])
882
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000883 def test_excessive_nesting(self):
884 # The original implementation would die with RecursionError here
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800885 with self.exit_stack() as stack:
Nick Coghlana5bd2a12012-06-01 00:00:38 +1000886 for i in range(10000):
887 stack.callback(int)
888
Nick Coghlan3267a302012-05-21 22:54:43 +1000889 def test_instance_bypass(self):
890 class Example(object): pass
891 cm = Example()
892 cm.__exit__ = object()
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800893 stack = self.exit_stack()
Nick Coghlan3267a302012-05-21 22:54:43 +1000894 self.assertRaises(AttributeError, stack.enter_context, cm)
895 stack.push(cm)
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800896 self.assertIs(stack._exit_callbacks[-1][1], cm)
Nick Coghlan3267a302012-05-21 22:54:43 +1000897
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700898 def test_dont_reraise_RuntimeError(self):
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300899 # https://bugs.python.org/issue27122
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700900 class UniqueException(Exception): pass
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300901 class UniqueRuntimeError(RuntimeError): pass
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700902
903 @contextmanager
904 def second():
905 try:
906 yield 1
907 except Exception as exc:
908 raise UniqueException("new exception") from exc
909
910 @contextmanager
911 def first():
912 try:
913 yield 1
914 except Exception as exc:
915 raise exc
916
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300917 # The UniqueRuntimeError should be caught by second()'s exception
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700918 # handler which chain raised a new UniqueException.
919 with self.assertRaises(UniqueException) as err_ctx:
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800920 with self.exit_stack() as es_ctx:
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700921 es_ctx.enter_context(second())
922 es_ctx.enter_context(first())
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300923 raise UniqueRuntimeError("please no infinite loop.")
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700924
Serhiy Storchakace1a9f32016-06-20 05:29:54 +0300925 exc = err_ctx.exception
926 self.assertIsInstance(exc, UniqueException)
927 self.assertIsInstance(exc.__context__, UniqueRuntimeError)
928 self.assertIsNone(exc.__context__.__context__)
929 self.assertIsNone(exc.__context__.__cause__)
930 self.assertIs(exc.__cause__, exc.__context__)
Gregory P. Smithba2ecd62016-06-14 09:19:20 -0700931
Berker Peksagbb44fe02014-11-28 23:28:06 +0200932
Ilya Kulakov1aa094f2018-01-25 12:51:18 -0800933class TestExitStack(TestBaseExitStack, unittest.TestCase):
934 exit_stack = ExitStack
935
936
Berker Peksagbb44fe02014-11-28 23:28:06 +0200937class TestRedirectStream:
938
939 redirect_stream = None
940 orig_stream = None
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700941
Nick Coghlan561eb5c2013-10-26 22:20:43 +1000942 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +1000943 def test_instance_docs(self):
944 # Issue 19330: ensure context manager instances have good docstrings
Berker Peksagbb44fe02014-11-28 23:28:06 +0200945 cm_docstring = self.redirect_stream.__doc__
946 obj = self.redirect_stream(None)
Nick Coghlan059def52013-10-26 18:08:15 +1000947 self.assertEqual(obj.__doc__, cm_docstring)
948
Nick Coghlan8e113b42013-11-03 17:00:51 +1000949 def test_no_redirect_in_init(self):
Berker Peksagbb44fe02014-11-28 23:28:06 +0200950 orig_stdout = getattr(sys, self.orig_stream)
951 self.redirect_stream(None)
952 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000953
Raymond Hettinger088cbf22013-10-10 00:46:57 -0700954 def test_redirect_to_string_io(self):
955 f = io.StringIO()
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000956 msg = "Consider an API like help(), which prints directly to stdout"
Berker Peksagbb44fe02014-11-28 23:28:06 +0200957 orig_stdout = getattr(sys, self.orig_stream)
958 with self.redirect_stream(f):
959 print(msg, file=getattr(sys, self.orig_stream))
960 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan0ddaed32013-10-26 16:37:47 +1000961 s = f.getvalue().strip()
962 self.assertEqual(s, msg)
Nick Coghlan3267a302012-05-21 22:54:43 +1000963
Nick Coghlan8608d262013-10-20 00:30:51 +1000964 def test_enter_result_is_target(self):
965 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200966 with self.redirect_stream(f) as enter_result:
Nick Coghlan8608d262013-10-20 00:30:51 +1000967 self.assertIs(enter_result, f)
968
969 def test_cm_is_reusable(self):
970 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200971 write_to_f = self.redirect_stream(f)
972 orig_stdout = 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("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000975 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200976 print("World!", file=getattr(sys, self.orig_stream))
977 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8608d262013-10-20 00:30:51 +1000978 s = f.getvalue()
979 self.assertEqual(s, "Hello World!\n")
980
Nick Coghlan8e113b42013-11-03 17:00:51 +1000981 def test_cm_is_reentrant(self):
Nick Coghlan8608d262013-10-20 00:30:51 +1000982 f = io.StringIO()
Berker Peksagbb44fe02014-11-28 23:28:06 +0200983 write_to_f = self.redirect_stream(f)
984 orig_stdout = getattr(sys, self.orig_stream)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000985 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200986 print("Hello", end=" ", file=getattr(sys, self.orig_stream))
Nick Coghlan8608d262013-10-20 00:30:51 +1000987 with write_to_f:
Berker Peksagbb44fe02014-11-28 23:28:06 +0200988 print("World!", file=getattr(sys, self.orig_stream))
989 self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
Nick Coghlan8e113b42013-11-03 17:00:51 +1000990 s = f.getvalue()
991 self.assertEqual(s, "Hello World!\n")
Nick Coghlan8608d262013-10-20 00:30:51 +1000992
993
Berker Peksagbb44fe02014-11-28 23:28:06 +0200994class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
995
996 redirect_stream = redirect_stdout
997 orig_stream = "stdout"
998
999
1000class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
1001
1002 redirect_stream = redirect_stderr
1003 orig_stream = "stderr"
1004
1005
Nick Coghlan240f86d2013-10-17 23:40:57 +10001006class TestSuppress(unittest.TestCase):
1007
Nick Coghlan561eb5c2013-10-26 22:20:43 +10001008 @support.requires_docstrings
Nick Coghlan059def52013-10-26 18:08:15 +10001009 def test_instance_docs(self):
1010 # Issue 19330: ensure context manager instances have good docstrings
1011 cm_docstring = suppress.__doc__
1012 obj = suppress()
1013 self.assertEqual(obj.__doc__, cm_docstring)
1014
Nick Coghlan8608d262013-10-20 00:30:51 +10001015 def test_no_result_from_enter(self):
1016 with suppress(ValueError) as enter_result:
1017 self.assertIsNone(enter_result)
Nick Coghlan240f86d2013-10-17 23:40:57 +10001018
Nick Coghlan8608d262013-10-20 00:30:51 +10001019 def test_no_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +10001020 with suppress(ValueError):
1021 self.assertEqual(pow(2, 5), 32)
1022
1023 def test_exact_exception(self):
Nick Coghlan240f86d2013-10-17 23:40:57 +10001024 with suppress(TypeError):
1025 len(5)
1026
Nick Coghlan059def52013-10-26 18:08:15 +10001027 def test_exception_hierarchy(self):
1028 with suppress(LookupError):
1029 'Hello'[50]
1030
1031 def test_other_exception(self):
1032 with self.assertRaises(ZeroDivisionError):
1033 with suppress(TypeError):
1034 1/0
1035
1036 def test_no_args(self):
1037 with self.assertRaises(ZeroDivisionError):
1038 with suppress():
1039 1/0
1040
Nick Coghlan240f86d2013-10-17 23:40:57 +10001041 def test_multiple_exception_args(self):
Nick Coghlan8608d262013-10-20 00:30:51 +10001042 with suppress(ZeroDivisionError, TypeError):
1043 1/0
Nick Coghlan240f86d2013-10-17 23:40:57 +10001044 with suppress(ZeroDivisionError, TypeError):
1045 len(5)
1046
Nick Coghlan8608d262013-10-20 00:30:51 +10001047 def test_cm_is_reentrant(self):
1048 ignore_exceptions = suppress(Exception)
1049 with ignore_exceptions:
1050 pass
1051 with ignore_exceptions:
1052 len(5)
1053 with ignore_exceptions:
Nick Coghlan8608d262013-10-20 00:30:51 +10001054 with ignore_exceptions: # Check nested usage
1055 len(5)
Martin Panter7c6420a2015-10-10 11:04:44 +00001056 outer_continued = True
1057 1/0
1058 self.assertTrue(outer_continued)
Nick Coghlan8608d262013-10-20 00:30:51 +10001059
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00001060if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -04001061 unittest.main()