blob: cbaafcf9230c6d5872687572f1eafc0693b8155f [file] [log] [blame]
Tim Peters400cbc32006-02-28 18:44:41 +00001"""Unit tests for the with statement specified in PEP 343."""
2
Thomas Wouters34aa7ba2006-02-28 19:02:24 +00003
Tim Peters400cbc32006-02-28 18:44:41 +00004__author__ = "Mike Bland"
5__email__ = "mbland at acm dot org"
6
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00007import sys
Tim Peters400cbc32006-02-28 18:44:41 +00008import unittest
Guido van Rossum1a5e21e2006-02-28 21:57:43 +00009from collections import deque
Antoine Pitrou67b212e2011-01-08 09:55:31 +000010from contextlib import _GeneratorContextManager, contextmanager
Benjamin Petersonee8712c2008-05-20 21:35:26 +000011from test.support import run_unittest
Tim Peters400cbc32006-02-28 18:44:41 +000012
13
Antoine Pitrou67b212e2011-01-08 09:55:31 +000014class MockContextManager(_GeneratorContextManager):
Serhiy Storchaka101ff352015-06-28 17:06:07 +030015 def __init__(self, *args):
16 super().__init__(*args)
Tim Peters400cbc32006-02-28 18:44:41 +000017 self.enter_called = False
18 self.exit_called = False
19 self.exit_args = None
20
Tim Peters400cbc32006-02-28 18:44:41 +000021 def __enter__(self):
22 self.enter_called = True
Antoine Pitrou67b212e2011-01-08 09:55:31 +000023 return _GeneratorContextManager.__enter__(self)
Tim Peters400cbc32006-02-28 18:44:41 +000024
25 def __exit__(self, type, value, traceback):
26 self.exit_called = True
27 self.exit_args = (type, value, traceback)
Antoine Pitrou67b212e2011-01-08 09:55:31 +000028 return _GeneratorContextManager.__exit__(self, type,
29 value, traceback)
Tim Peters400cbc32006-02-28 18:44:41 +000030
31
32def mock_contextmanager(func):
33 def helper(*args, **kwds):
Serhiy Storchaka101ff352015-06-28 17:06:07 +030034 return MockContextManager(func, args, kwds)
Tim Peters400cbc32006-02-28 18:44:41 +000035 return helper
36
37
38class MockResource(object):
39 def __init__(self):
40 self.yielded = False
41 self.stopped = False
42
43
44@mock_contextmanager
45def mock_contextmanager_generator():
46 mock = MockResource()
47 try:
48 mock.yielded = True
49 yield mock
50 finally:
51 mock.stopped = True
52
53
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000054class Nested(object):
55
Thomas Wouters477c8d52006-05-27 19:21:47 +000056 def __init__(self, *managers):
57 self.managers = managers
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000058 self.entered = None
59
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000060 def __enter__(self):
61 if self.entered is not None:
62 raise RuntimeError("Context is not reentrant")
63 self.entered = deque()
64 vars = []
65 try:
Thomas Wouters477c8d52006-05-27 19:21:47 +000066 for mgr in self.managers:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000067 vars.append(mgr.__enter__())
68 self.entered.appendleft(mgr)
69 except:
Guido van Rossumf6694362006-03-10 02:28:35 +000070 if not self.__exit__(*sys.exc_info()):
71 raise
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000072 return vars
73
74 def __exit__(self, *exc_info):
75 # Behave like nested with statements
76 # first in, last out
77 # New exceptions override old ones
78 ex = exc_info
79 for mgr in self.entered:
80 try:
Guido van Rossumf6694362006-03-10 02:28:35 +000081 if mgr.__exit__(*ex):
82 ex = (None, None, None)
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000083 except:
84 ex = sys.exc_info()
85 self.entered = None
86 if ex is not exc_info:
Collin Winter828f04a2007-08-31 00:04:24 +000087 raise ex[0](ex[1]).with_traceback(ex[2])
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000088
89
90class MockNested(Nested):
Thomas Wouters477c8d52006-05-27 19:21:47 +000091 def __init__(self, *managers):
92 Nested.__init__(self, *managers)
Tim Peters400cbc32006-02-28 18:44:41 +000093 self.enter_called = False
94 self.exit_called = False
95 self.exit_args = None
96
Tim Peters400cbc32006-02-28 18:44:41 +000097 def __enter__(self):
98 self.enter_called = True
Guido van Rossum1a5e21e2006-02-28 21:57:43 +000099 return Nested.__enter__(self)
Tim Peters400cbc32006-02-28 18:44:41 +0000100
101 def __exit__(self, *exc_info):
102 self.exit_called = True
103 self.exit_args = exc_info
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000104 return Nested.__exit__(self, *exc_info)
Tim Peters400cbc32006-02-28 18:44:41 +0000105
106
107class FailureTestCase(unittest.TestCase):
108 def testNameError(self):
109 def fooNotDeclared():
110 with foo: pass
111 self.assertRaises(NameError, fooNotDeclared)
112
Tim Peters400cbc32006-02-28 18:44:41 +0000113 def testEnterAttributeError(self):
114 class LacksEnter(object):
Tim Peters400cbc32006-02-28 18:44:41 +0000115 def __exit__(self, type, value, traceback):
116 pass
117
118 def fooLacksEnter():
119 foo = LacksEnter()
120 with foo: pass
121 self.assertRaises(AttributeError, fooLacksEnter)
122
123 def testExitAttributeError(self):
124 class LacksExit(object):
Tim Peters400cbc32006-02-28 18:44:41 +0000125 def __enter__(self):
126 pass
127
128 def fooLacksExit():
129 foo = LacksExit()
130 with foo: pass
131 self.assertRaises(AttributeError, fooLacksExit)
132
133 def assertRaisesSyntaxError(self, codestr):
134 def shouldRaiseSyntaxError(s):
135 compile(s, '', 'single')
136 self.assertRaises(SyntaxError, shouldRaiseSyntaxError, codestr)
137
138 def testAssignmentToNoneError(self):
139 self.assertRaisesSyntaxError('with mock as None:\n pass')
140 self.assertRaisesSyntaxError(
141 'with mock as (None):\n'
142 ' pass')
143
144 def testAssignmentToEmptyTupleError(self):
145 self.assertRaisesSyntaxError(
146 'with mock as ():\n'
147 ' pass')
148
149 def testAssignmentToTupleOnlyContainingNoneError(self):
150 self.assertRaisesSyntaxError('with mock as None,:\n pass')
151 self.assertRaisesSyntaxError(
152 'with mock as (None,):\n'
153 ' pass')
154
155 def testAssignmentToTupleContainingNoneError(self):
156 self.assertRaisesSyntaxError(
157 'with mock as (foo, None, bar):\n'
158 ' pass')
159
Tim Peters400cbc32006-02-28 18:44:41 +0000160 def testEnterThrows(self):
161 class EnterThrows(object):
Tim Peters400cbc32006-02-28 18:44:41 +0000162 def __enter__(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000163 raise RuntimeError("Enter threw")
Tim Peters400cbc32006-02-28 18:44:41 +0000164 def __exit__(self, *args):
165 pass
166
167 def shouldThrow():
168 ct = EnterThrows()
169 self.foo = None
170 with ct as self.foo:
171 pass
172 self.assertRaises(RuntimeError, shouldThrow)
173 self.assertEqual(self.foo, None)
174
175 def testExitThrows(self):
176 class ExitThrows(object):
Tim Peters400cbc32006-02-28 18:44:41 +0000177 def __enter__(self):
178 return
179 def __exit__(self, *args):
180 raise RuntimeError(42)
181 def shouldThrow():
182 with ExitThrows():
183 pass
184 self.assertRaises(RuntimeError, shouldThrow)
185
186class ContextmanagerAssertionMixin(object):
Collin Winterba21f612007-09-01 20:29:04 +0000187
188 def setUp(self):
189 self.TEST_EXCEPTION = RuntimeError("test exception")
Tim Peters400cbc32006-02-28 18:44:41 +0000190
191 def assertInWithManagerInvariants(self, mock_manager):
Tim Peters400cbc32006-02-28 18:44:41 +0000192 self.assertTrue(mock_manager.enter_called)
193 self.assertFalse(mock_manager.exit_called)
194 self.assertEqual(mock_manager.exit_args, None)
195
196 def assertAfterWithManagerInvariants(self, mock_manager, exit_args):
Tim Peters400cbc32006-02-28 18:44:41 +0000197 self.assertTrue(mock_manager.enter_called)
198 self.assertTrue(mock_manager.exit_called)
199 self.assertEqual(mock_manager.exit_args, exit_args)
200
201 def assertAfterWithManagerInvariantsNoError(self, mock_manager):
202 self.assertAfterWithManagerInvariants(mock_manager,
203 (None, None, None))
204
205 def assertInWithGeneratorInvariants(self, mock_generator):
206 self.assertTrue(mock_generator.yielded)
207 self.assertFalse(mock_generator.stopped)
208
209 def assertAfterWithGeneratorInvariantsNoError(self, mock_generator):
210 self.assertTrue(mock_generator.yielded)
211 self.assertTrue(mock_generator.stopped)
212
213 def raiseTestException(self):
214 raise self.TEST_EXCEPTION
215
Benjamin Peterson77aa6a72010-02-05 02:07:05 +0000216 def assertAfterWithManagerInvariantsWithError(self, mock_manager,
217 exc_type=None):
Tim Peters400cbc32006-02-28 18:44:41 +0000218 self.assertTrue(mock_manager.enter_called)
219 self.assertTrue(mock_manager.exit_called)
Benjamin Peterson77aa6a72010-02-05 02:07:05 +0000220 if exc_type is None:
221 self.assertEqual(mock_manager.exit_args[1], self.TEST_EXCEPTION)
222 exc_type = type(self.TEST_EXCEPTION)
223 self.assertEqual(mock_manager.exit_args[0], exc_type)
224 # Test the __exit__ arguments. Issue #7853
225 self.assertIsInstance(mock_manager.exit_args[1], exc_type)
226 self.assertIsNot(mock_manager.exit_args[2], None)
Tim Peters400cbc32006-02-28 18:44:41 +0000227
228 def assertAfterWithGeneratorInvariantsWithError(self, mock_generator):
229 self.assertTrue(mock_generator.yielded)
230 self.assertTrue(mock_generator.stopped)
231
232
233class NonexceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
234 def testInlineGeneratorSyntax(self):
235 with mock_contextmanager_generator():
236 pass
237
238 def testUnboundGenerator(self):
239 mock = mock_contextmanager_generator()
240 with mock:
241 pass
242 self.assertAfterWithManagerInvariantsNoError(mock)
243
244 def testInlineGeneratorBoundSyntax(self):
245 with mock_contextmanager_generator() as foo:
246 self.assertInWithGeneratorInvariants(foo)
247 # FIXME: In the future, we'll try to keep the bound names from leaking
248 self.assertAfterWithGeneratorInvariantsNoError(foo)
249
250 def testInlineGeneratorBoundToExistingVariable(self):
251 foo = None
252 with mock_contextmanager_generator() as foo:
253 self.assertInWithGeneratorInvariants(foo)
254 self.assertAfterWithGeneratorInvariantsNoError(foo)
255
256 def testInlineGeneratorBoundToDottedVariable(self):
257 with mock_contextmanager_generator() as self.foo:
258 self.assertInWithGeneratorInvariants(self.foo)
259 self.assertAfterWithGeneratorInvariantsNoError(self.foo)
260
261 def testBoundGenerator(self):
262 mock = mock_contextmanager_generator()
263 with mock as foo:
264 self.assertInWithGeneratorInvariants(foo)
265 self.assertInWithManagerInvariants(mock)
266 self.assertAfterWithGeneratorInvariantsNoError(foo)
267 self.assertAfterWithManagerInvariantsNoError(mock)
268
269 def testNestedSingleStatements(self):
270 mock_a = mock_contextmanager_generator()
271 with mock_a as foo:
272 mock_b = mock_contextmanager_generator()
273 with mock_b as bar:
274 self.assertInWithManagerInvariants(mock_a)
275 self.assertInWithManagerInvariants(mock_b)
276 self.assertInWithGeneratorInvariants(foo)
277 self.assertInWithGeneratorInvariants(bar)
278 self.assertAfterWithManagerInvariantsNoError(mock_b)
279 self.assertAfterWithGeneratorInvariantsNoError(bar)
280 self.assertInWithManagerInvariants(mock_a)
281 self.assertInWithGeneratorInvariants(foo)
282 self.assertAfterWithManagerInvariantsNoError(mock_a)
283 self.assertAfterWithGeneratorInvariantsNoError(foo)
284
285
286class NestedNonexceptionalTestCase(unittest.TestCase,
287 ContextmanagerAssertionMixin):
288 def testSingleArgInlineGeneratorSyntax(self):
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000289 with Nested(mock_contextmanager_generator()):
Tim Peters400cbc32006-02-28 18:44:41 +0000290 pass
291
Tim Peters400cbc32006-02-28 18:44:41 +0000292 def testSingleArgBoundToNonTuple(self):
293 m = mock_contextmanager_generator()
294 # This will bind all the arguments to nested() into a single list
295 # assigned to foo.
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000296 with Nested(m) as foo:
Tim Peters400cbc32006-02-28 18:44:41 +0000297 self.assertInWithManagerInvariants(m)
298 self.assertAfterWithManagerInvariantsNoError(m)
299
300 def testSingleArgBoundToSingleElementParenthesizedList(self):
301 m = mock_contextmanager_generator()
302 # This will bind all the arguments to nested() into a single list
303 # assigned to foo.
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000304 with Nested(m) as (foo):
Tim Peters400cbc32006-02-28 18:44:41 +0000305 self.assertInWithManagerInvariants(m)
306 self.assertAfterWithManagerInvariantsNoError(m)
307
308 def testSingleArgBoundToMultipleElementTupleError(self):
309 def shouldThrowValueError():
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000310 with Nested(mock_contextmanager_generator()) as (foo, bar):
Tim Peters400cbc32006-02-28 18:44:41 +0000311 pass
312 self.assertRaises(ValueError, shouldThrowValueError)
313
314 def testSingleArgUnbound(self):
315 mock_contextmanager = mock_contextmanager_generator()
316 mock_nested = MockNested(mock_contextmanager)
317 with mock_nested:
318 self.assertInWithManagerInvariants(mock_contextmanager)
319 self.assertInWithManagerInvariants(mock_nested)
320 self.assertAfterWithManagerInvariantsNoError(mock_contextmanager)
321 self.assertAfterWithManagerInvariantsNoError(mock_nested)
322
323 def testMultipleArgUnbound(self):
324 m = mock_contextmanager_generator()
325 n = mock_contextmanager_generator()
326 o = mock_contextmanager_generator()
327 mock_nested = MockNested(m, n, o)
328 with mock_nested:
329 self.assertInWithManagerInvariants(m)
330 self.assertInWithManagerInvariants(n)
331 self.assertInWithManagerInvariants(o)
332 self.assertInWithManagerInvariants(mock_nested)
333 self.assertAfterWithManagerInvariantsNoError(m)
334 self.assertAfterWithManagerInvariantsNoError(n)
335 self.assertAfterWithManagerInvariantsNoError(o)
336 self.assertAfterWithManagerInvariantsNoError(mock_nested)
337
338 def testMultipleArgBound(self):
339 mock_nested = MockNested(mock_contextmanager_generator(),
340 mock_contextmanager_generator(), mock_contextmanager_generator())
341 with mock_nested as (m, n, o):
342 self.assertInWithGeneratorInvariants(m)
343 self.assertInWithGeneratorInvariants(n)
344 self.assertInWithGeneratorInvariants(o)
345 self.assertInWithManagerInvariants(mock_nested)
346 self.assertAfterWithGeneratorInvariantsNoError(m)
347 self.assertAfterWithGeneratorInvariantsNoError(n)
348 self.assertAfterWithGeneratorInvariantsNoError(o)
349 self.assertAfterWithManagerInvariantsNoError(mock_nested)
350
351
Collin Winterba21f612007-09-01 20:29:04 +0000352class ExceptionalTestCase(ContextmanagerAssertionMixin, unittest.TestCase):
Tim Peters400cbc32006-02-28 18:44:41 +0000353 def testSingleResource(self):
354 cm = mock_contextmanager_generator()
355 def shouldThrow():
356 with cm as self.resource:
357 self.assertInWithManagerInvariants(cm)
358 self.assertInWithGeneratorInvariants(self.resource)
359 self.raiseTestException()
360 self.assertRaises(RuntimeError, shouldThrow)
361 self.assertAfterWithManagerInvariantsWithError(cm)
362 self.assertAfterWithGeneratorInvariantsWithError(self.resource)
363
Benjamin Peterson77aa6a72010-02-05 02:07:05 +0000364 def testExceptionNormalized(self):
365 cm = mock_contextmanager_generator()
366 def shouldThrow():
367 with cm as self.resource:
368 # Note this relies on the fact that 1 // 0 produces an exception
369 # that is not normalized immediately.
370 1 // 0
371 self.assertRaises(ZeroDivisionError, shouldThrow)
372 self.assertAfterWithManagerInvariantsWithError(cm, ZeroDivisionError)
373
Tim Peters400cbc32006-02-28 18:44:41 +0000374 def testNestedSingleStatements(self):
375 mock_a = mock_contextmanager_generator()
376 mock_b = mock_contextmanager_generator()
377 def shouldThrow():
378 with mock_a as self.foo:
379 with mock_b as self.bar:
380 self.assertInWithManagerInvariants(mock_a)
381 self.assertInWithManagerInvariants(mock_b)
382 self.assertInWithGeneratorInvariants(self.foo)
383 self.assertInWithGeneratorInvariants(self.bar)
384 self.raiseTestException()
385 self.assertRaises(RuntimeError, shouldThrow)
386 self.assertAfterWithManagerInvariantsWithError(mock_a)
387 self.assertAfterWithManagerInvariantsWithError(mock_b)
388 self.assertAfterWithGeneratorInvariantsWithError(self.foo)
389 self.assertAfterWithGeneratorInvariantsWithError(self.bar)
390
391 def testMultipleResourcesInSingleStatement(self):
392 cm_a = mock_contextmanager_generator()
393 cm_b = mock_contextmanager_generator()
394 mock_nested = MockNested(cm_a, cm_b)
395 def shouldThrow():
396 with mock_nested as (self.resource_a, self.resource_b):
397 self.assertInWithManagerInvariants(cm_a)
398 self.assertInWithManagerInvariants(cm_b)
399 self.assertInWithManagerInvariants(mock_nested)
400 self.assertInWithGeneratorInvariants(self.resource_a)
401 self.assertInWithGeneratorInvariants(self.resource_b)
402 self.raiseTestException()
403 self.assertRaises(RuntimeError, shouldThrow)
404 self.assertAfterWithManagerInvariantsWithError(cm_a)
405 self.assertAfterWithManagerInvariantsWithError(cm_b)
406 self.assertAfterWithManagerInvariantsWithError(mock_nested)
407 self.assertAfterWithGeneratorInvariantsWithError(self.resource_a)
408 self.assertAfterWithGeneratorInvariantsWithError(self.resource_b)
409
410 def testNestedExceptionBeforeInnerStatement(self):
411 mock_a = mock_contextmanager_generator()
412 mock_b = mock_contextmanager_generator()
413 self.bar = None
414 def shouldThrow():
415 with mock_a as self.foo:
416 self.assertInWithManagerInvariants(mock_a)
417 self.assertInWithGeneratorInvariants(self.foo)
418 self.raiseTestException()
419 with mock_b as self.bar:
420 pass
421 self.assertRaises(RuntimeError, shouldThrow)
422 self.assertAfterWithManagerInvariantsWithError(mock_a)
423 self.assertAfterWithGeneratorInvariantsWithError(self.foo)
424
425 # The inner statement stuff should never have been touched
426 self.assertEqual(self.bar, None)
Tim Peters400cbc32006-02-28 18:44:41 +0000427 self.assertFalse(mock_b.enter_called)
428 self.assertFalse(mock_b.exit_called)
429 self.assertEqual(mock_b.exit_args, None)
430
431 def testNestedExceptionAfterInnerStatement(self):
432 mock_a = mock_contextmanager_generator()
433 mock_b = mock_contextmanager_generator()
434 def shouldThrow():
435 with mock_a as self.foo:
436 with mock_b as self.bar:
437 self.assertInWithManagerInvariants(mock_a)
438 self.assertInWithManagerInvariants(mock_b)
439 self.assertInWithGeneratorInvariants(self.foo)
440 self.assertInWithGeneratorInvariants(self.bar)
441 self.raiseTestException()
442 self.assertRaises(RuntimeError, shouldThrow)
443 self.assertAfterWithManagerInvariantsWithError(mock_a)
444 self.assertAfterWithManagerInvariantsNoError(mock_b)
445 self.assertAfterWithGeneratorInvariantsWithError(self.foo)
446 self.assertAfterWithGeneratorInvariantsNoError(self.bar)
447
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000448 def testRaisedStopIteration1(self):
Guido van Rossum2cc30da2007-11-02 23:46:40 +0000449 # From bug 1462485
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000450 @contextmanager
451 def cm():
452 yield
453
454 def shouldThrow():
455 with cm():
456 raise StopIteration("from with")
457
458 self.assertRaises(StopIteration, shouldThrow)
459
460 def testRaisedStopIteration2(self):
Guido van Rossum2cc30da2007-11-02 23:46:40 +0000461 # From bug 1462485
Thomas Wouters477c8d52006-05-27 19:21:47 +0000462 class cm(object):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000463 def __enter__(self):
464 pass
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000465 def __exit__(self, type, value, traceback):
466 pass
467
468 def shouldThrow():
469 with cm():
470 raise StopIteration("from with")
471
472 self.assertRaises(StopIteration, shouldThrow)
473
Guido van Rossum2cc30da2007-11-02 23:46:40 +0000474 def testRaisedStopIteration3(self):
475 # Another variant where the exception hasn't been instantiated
476 # From bug 1705170
477 @contextmanager
478 def cm():
479 yield
480
481 def shouldThrow():
482 with cm():
483 raise next(iter([]))
484
485 self.assertRaises(StopIteration, shouldThrow)
486
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000487 def testRaisedGeneratorExit1(self):
Guido van Rossum2cc30da2007-11-02 23:46:40 +0000488 # From bug 1462485
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000489 @contextmanager
490 def cm():
491 yield
492
493 def shouldThrow():
494 with cm():
495 raise GeneratorExit("from with")
496
497 self.assertRaises(GeneratorExit, shouldThrow)
498
499 def testRaisedGeneratorExit2(self):
Guido van Rossum2cc30da2007-11-02 23:46:40 +0000500 # From bug 1462485
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000501 class cm (object):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000502 def __enter__(self):
503 pass
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000504 def __exit__(self, type, value, traceback):
505 pass
506
507 def shouldThrow():
508 with cm():
509 raise GeneratorExit("from with")
510
511 self.assertRaises(GeneratorExit, shouldThrow)
512
Amaury Forgeot d'Arc10b24e82008-12-10 23:49:33 +0000513 def testErrorsInBool(self):
514 # issue4589: __exit__ return code may raise an exception
515 # when looking at its truth value.
516
517 class cm(object):
518 def __init__(self, bool_conversion):
519 class Bool:
520 def __bool__(self):
521 return bool_conversion()
522 self.exit_result = Bool()
523 def __enter__(self):
524 return 3
525 def __exit__(self, a, b, c):
526 return self.exit_result
527
528 def trueAsBool():
529 with cm(lambda: True):
530 self.fail("Should NOT see this")
531 trueAsBool()
532
533 def falseAsBool():
534 with cm(lambda: False):
535 self.fail("Should raise")
536 self.assertRaises(AssertionError, falseAsBool)
537
538 def failAsBool():
539 with cm(lambda: 1//0):
540 self.fail("Should NOT see this")
541 self.assertRaises(ZeroDivisionError, failAsBool)
542
Tim Peters400cbc32006-02-28 18:44:41 +0000543
544class NonLocalFlowControlTestCase(unittest.TestCase):
545
546 def testWithBreak(self):
547 counter = 0
548 while True:
549 counter += 1
550 with mock_contextmanager_generator():
551 counter += 10
552 break
553 counter += 100 # Not reached
554 self.assertEqual(counter, 11)
555
556 def testWithContinue(self):
557 counter = 0
558 while True:
559 counter += 1
560 if counter > 2:
561 break
562 with mock_contextmanager_generator():
563 counter += 10
564 continue
565 counter += 100 # Not reached
566 self.assertEqual(counter, 12)
567
568 def testWithReturn(self):
569 def foo():
570 counter = 0
571 while True:
572 counter += 1
573 with mock_contextmanager_generator():
574 counter += 10
575 return counter
576 counter += 100 # Not reached
577 self.assertEqual(foo(), 11)
578
579 def testWithYield(self):
580 def gen():
581 with mock_contextmanager_generator():
582 yield 12
583 yield 13
584 x = list(gen())
585 self.assertEqual(x, [12, 13])
586
587 def testWithRaise(self):
588 counter = 0
589 try:
590 counter += 1
591 with mock_contextmanager_generator():
592 counter += 10
593 raise RuntimeError
594 counter += 100 # Not reached
595 except RuntimeError:
596 self.assertEqual(counter, 11)
597 else:
598 self.fail("Didn't raise RuntimeError")
599
600
601class AssignmentTargetTestCase(unittest.TestCase):
602
603 def testSingleComplexTarget(self):
604 targets = {1: [0, 1, 2]}
605 with mock_contextmanager_generator() as targets[1][0]:
Guido van Rossum87b63952007-02-11 07:05:21 +0000606 self.assertEqual(list(targets.keys()), [1])
Tim Peters400cbc32006-02-28 18:44:41 +0000607 self.assertEqual(targets[1][0].__class__, MockResource)
Guido van Rossum87b63952007-02-11 07:05:21 +0000608 with mock_contextmanager_generator() as list(targets.values())[0][1]:
609 self.assertEqual(list(targets.keys()), [1])
Tim Peters400cbc32006-02-28 18:44:41 +0000610 self.assertEqual(targets[1][1].__class__, MockResource)
611 with mock_contextmanager_generator() as targets[2]:
Guido van Rossum87b63952007-02-11 07:05:21 +0000612 keys = list(targets.keys())
Tim Peters400cbc32006-02-28 18:44:41 +0000613 keys.sort()
614 self.assertEqual(keys, [1, 2])
615 class C: pass
616 blah = C()
617 with mock_contextmanager_generator() as blah.foo:
618 self.assertEqual(hasattr(blah, "foo"), True)
619
620 def testMultipleComplexTargets(self):
621 class C:
Tim Peters400cbc32006-02-28 18:44:41 +0000622 def __enter__(self): return 1, 2, 3
Guido van Rossumf6694362006-03-10 02:28:35 +0000623 def __exit__(self, t, v, tb): pass
Tim Peters400cbc32006-02-28 18:44:41 +0000624 targets = {1: [0, 1, 2]}
625 with C() as (targets[1][0], targets[1][1], targets[1][2]):
626 self.assertEqual(targets, {1: [1, 2, 3]})
Guido van Rossum87b63952007-02-11 07:05:21 +0000627 with C() as (list(targets.values())[0][2], list(targets.values())[0][1], list(targets.values())[0][0]):
Tim Peters400cbc32006-02-28 18:44:41 +0000628 self.assertEqual(targets, {1: [3, 2, 1]})
629 with C() as (targets[1], targets[2], targets[3]):
630 self.assertEqual(targets, {1: 1, 2: 2, 3: 3})
631 class B: pass
632 blah = B()
633 with C() as (blah.one, blah.two, blah.three):
634 self.assertEqual(blah.one, 1)
635 self.assertEqual(blah.two, 2)
636 self.assertEqual(blah.three, 3)
637
638
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000639class ExitSwallowsExceptionTestCase(unittest.TestCase):
640
Guido van Rossumf6694362006-03-10 02:28:35 +0000641 def testExitTrueSwallowsException(self):
642 class AfricanSwallow:
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000643 def __enter__(self): pass
Guido van Rossumf6694362006-03-10 02:28:35 +0000644 def __exit__(self, t, v, tb): return True
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000645 try:
Guido van Rossumf6694362006-03-10 02:28:35 +0000646 with AfricanSwallow():
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000647 1/0
648 except ZeroDivisionError:
649 self.fail("ZeroDivisionError should have been swallowed")
650
Guido van Rossumf6694362006-03-10 02:28:35 +0000651 def testExitFalseDoesntSwallowException(self):
652 class EuropeanSwallow:
Guido van Rossumf6694362006-03-10 02:28:35 +0000653 def __enter__(self): pass
654 def __exit__(self, t, v, tb): return False
655 try:
656 with EuropeanSwallow():
657 1/0
658 except ZeroDivisionError:
659 pass
660 else:
661 self.fail("ZeroDivisionError should have been raised")
662
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000663
Georg Brandl0c315622009-05-25 21:10:36 +0000664class NestedWith(unittest.TestCase):
665
666 class Dummy(object):
667 def __init__(self, value=None, gobble=False):
668 if value is None:
669 value = self
670 self.value = value
671 self.gobble = gobble
672 self.enter_called = False
673 self.exit_called = False
674
675 def __enter__(self):
676 self.enter_called = True
677 return self.value
678
679 def __exit__(self, *exc_info):
680 self.exit_called = True
681 self.exc_info = exc_info
682 if self.gobble:
683 return True
684
Georg Brandl3cfdd9c2009-06-04 10:21:10 +0000685 class InitRaises(object):
Georg Brandl0c315622009-05-25 21:10:36 +0000686 def __init__(self): raise RuntimeError()
687
688 class EnterRaises(object):
689 def __enter__(self): raise RuntimeError()
690 def __exit__(self, *exc_info): pass
691
692 class ExitRaises(object):
693 def __enter__(self): pass
694 def __exit__(self, *exc_info): raise RuntimeError()
695
696 def testNoExceptions(self):
697 with self.Dummy() as a, self.Dummy() as b:
698 self.assertTrue(a.enter_called)
699 self.assertTrue(b.enter_called)
700 self.assertTrue(a.exit_called)
701 self.assertTrue(b.exit_called)
702
703 def testExceptionInExprList(self):
704 try:
Georg Brandl3cfdd9c2009-06-04 10:21:10 +0000705 with self.Dummy() as a, self.InitRaises():
Georg Brandl0c315622009-05-25 21:10:36 +0000706 pass
707 except:
708 pass
709 self.assertTrue(a.enter_called)
710 self.assertTrue(a.exit_called)
711
712 def testExceptionInEnter(self):
713 try:
714 with self.Dummy() as a, self.EnterRaises():
715 self.fail('body of bad with executed')
716 except RuntimeError:
717 pass
718 else:
719 self.fail('RuntimeError not reraised')
720 self.assertTrue(a.enter_called)
721 self.assertTrue(a.exit_called)
722
723 def testExceptionInExit(self):
724 body_executed = False
725 with self.Dummy(gobble=True) as a, self.ExitRaises():
726 body_executed = True
727 self.assertTrue(a.enter_called)
728 self.assertTrue(a.exit_called)
Benjamin Peterson78565b22009-06-28 19:19:51 +0000729 self.assertTrue(body_executed)
Georg Brandl0c315622009-05-25 21:10:36 +0000730 self.assertNotEqual(a.exc_info[0], None)
731
732 def testEnterReturnsTuple(self):
733 with self.Dummy(value=(1,2)) as (a1, a2), \
734 self.Dummy(value=(10, 20)) as (b1, b2):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000735 self.assertEqual(1, a1)
736 self.assertEqual(2, a2)
737 self.assertEqual(10, b1)
738 self.assertEqual(20, b2)
Georg Brandl0c315622009-05-25 21:10:36 +0000739
Tim Peters400cbc32006-02-28 18:44:41 +0000740def test_main():
741 run_unittest(FailureTestCase, NonexceptionalTestCase,
742 NestedNonexceptionalTestCase, ExceptionalTestCase,
743 NonLocalFlowControlTestCase,
Guido van Rossum1a5e21e2006-02-28 21:57:43 +0000744 AssignmentTargetTestCase,
Georg Brandl0c315622009-05-25 21:10:36 +0000745 ExitSwallowsExceptionTestCase,
746 NestedWith)
Tim Peters400cbc32006-02-28 18:44:41 +0000747
748
749if __name__ == '__main__':
750 test_main()