3 """Unit tests for the with statement specified in PEP 343."""
6 __author__
= "Mike Bland"
7 __email__
= "mbland at acm dot org"
11 from collections
import deque
12 from contextlib
import GeneratorContextManager
, contextmanager
13 from test
.test_support
import run_unittest
16 class MockContextManager(GeneratorContextManager
):
17 def __init__(self
, gen
):
18 GeneratorContextManager
.__init
__(self
, gen
)
19 self
.enter_called
= False
20 self
.exit_called
= False
24 self
.enter_called
= True
25 return GeneratorContextManager
.__enter
__(self
)
27 def __exit__(self
, type, value
, traceback
):
28 self
.exit_called
= True
29 self
.exit_args
= (type, value
, traceback
)
30 return GeneratorContextManager
.__exit
__(self
, type,
34 def mock_contextmanager(func
):
35 def helper(*args
, **kwds
):
36 return MockContextManager(func(*args
, **kwds
))
40 class MockResource(object):
47 def mock_contextmanager_generator():
58 def __init__(self
, *managers
):
59 self
.managers
= managers
63 if self
.entered
is not None:
64 raise RuntimeError("Context is not reentrant")
65 self
.entered
= deque()
68 for mgr
in self
.managers
:
69 vars.append(mgr
.__enter
__())
70 self
.entered
.appendleft(mgr
)
72 if not self
.__exit
__(*sys
.exc_info()):
76 def __exit__(self
, *exc_info
):
77 # Behave like nested with statements
79 # New exceptions override old ones
81 for mgr
in self
.entered
:
84 ex
= (None, None, None)
88 if ex
is not exc_info
:
89 raise ex
[0], ex
[1], ex
[2]
92 class MockNested(Nested
):
93 def __init__(self
, *managers
):
94 Nested
.__init
__(self
, *managers
)
95 self
.enter_called
= False
96 self
.exit_called
= False
100 self
.enter_called
= True
101 return Nested
.__enter
__(self
)
103 def __exit__(self
, *exc_info
):
104 self
.exit_called
= True
105 self
.exit_args
= exc_info
106 return Nested
.__exit
__(self
, *exc_info
)
109 class FailureTestCase(unittest
.TestCase
):
110 def testNameError(self
):
111 def fooNotDeclared():
113 self
.assertRaises(NameError, fooNotDeclared
)
115 def testEnterAttributeError(self
):
116 class LacksEnter(object):
117 def __exit__(self
, type, value
, traceback
):
123 self
.assertRaises(AttributeError, fooLacksEnter
)
125 def testExitAttributeError(self
):
126 class LacksExit(object):
133 self
.assertRaises(AttributeError, fooLacksExit
)
135 def assertRaisesSyntaxError(self
, codestr
):
136 def shouldRaiseSyntaxError(s
):
137 compile(s
, '', 'single')
138 self
.assertRaises(SyntaxError, shouldRaiseSyntaxError
, codestr
)
140 def testAssignmentToNoneError(self
):
141 self
.assertRaisesSyntaxError('with mock as None:\n pass')
142 self
.assertRaisesSyntaxError(
143 'with mock as (None):\n'
146 def testAssignmentToEmptyTupleError(self
):
147 self
.assertRaisesSyntaxError(
151 def testAssignmentToTupleOnlyContainingNoneError(self
):
152 self
.assertRaisesSyntaxError('with mock as None,:\n pass')
153 self
.assertRaisesSyntaxError(
154 'with mock as (None,):\n'
157 def testAssignmentToTupleContainingNoneError(self
):
158 self
.assertRaisesSyntaxError(
159 'with mock as (foo, None, bar):\n'
162 def testEnterThrows(self
):
163 class EnterThrows(object):
165 raise RuntimeError("Enter threw")
166 def __exit__(self
, *args
):
174 self
.assertRaises(RuntimeError, shouldThrow
)
175 self
.assertEqual(self
.foo
, None)
177 def testExitThrows(self
):
178 class ExitThrows(object):
181 def __exit__(self
, *args
):
182 raise RuntimeError(42)
186 self
.assertRaises(RuntimeError, shouldThrow
)
188 class ContextmanagerAssertionMixin(object):
189 TEST_EXCEPTION
= RuntimeError("test exception")
191 def assertInWithManagerInvariants(self
, mock_manager
):
192 self
.assertTrue(mock_manager
.enter_called
)
193 self
.assertFalse(mock_manager
.exit_called
)
194 self
.assertEqual(mock_manager
.exit_args
, None)
196 def assertAfterWithManagerInvariants(self
, mock_manager
, exit_args
):
197 self
.assertTrue(mock_manager
.enter_called
)
198 self
.assertTrue(mock_manager
.exit_called
)
199 self
.assertEqual(mock_manager
.exit_args
, exit_args
)
201 def assertAfterWithManagerInvariantsNoError(self
, mock_manager
):
202 self
.assertAfterWithManagerInvariants(mock_manager
,
205 def assertInWithGeneratorInvariants(self
, mock_generator
):
206 self
.assertTrue(mock_generator
.yielded
)
207 self
.assertFalse(mock_generator
.stopped
)
209 def assertAfterWithGeneratorInvariantsNoError(self
, mock_generator
):
210 self
.assertTrue(mock_generator
.yielded
)
211 self
.assertTrue(mock_generator
.stopped
)
213 def raiseTestException(self
):
214 raise self
.TEST_EXCEPTION
216 def assertAfterWithManagerInvariantsWithError(self
, mock_manager
,
218 self
.assertTrue(mock_manager
.enter_called
)
219 self
.assertTrue(mock_manager
.exit_called
)
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)
228 def assertAfterWithGeneratorInvariantsWithError(self
, mock_generator
):
229 self
.assertTrue(mock_generator
.yielded
)
230 self
.assertTrue(mock_generator
.stopped
)
233 class NonexceptionalTestCase(unittest
.TestCase
, ContextmanagerAssertionMixin
):
234 def testInlineGeneratorSyntax(self
):
235 with
mock_contextmanager_generator():
238 def testUnboundGenerator(self
):
239 mock
= mock_contextmanager_generator()
242 self
.assertAfterWithManagerInvariantsNoError(mock
)
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
)
250 def testInlineGeneratorBoundToExistingVariable(self
):
252 with
mock_contextmanager_generator() as foo
:
253 self
.assertInWithGeneratorInvariants(foo
)
254 self
.assertAfterWithGeneratorInvariantsNoError(foo
)
256 def testInlineGeneratorBoundToDottedVariable(self
):
257 with
mock_contextmanager_generator() as self
.foo
:
258 self
.assertInWithGeneratorInvariants(self
.foo
)
259 self
.assertAfterWithGeneratorInvariantsNoError(self
.foo
)
261 def testBoundGenerator(self
):
262 mock
= mock_contextmanager_generator()
264 self
.assertInWithGeneratorInvariants(foo
)
265 self
.assertInWithManagerInvariants(mock
)
266 self
.assertAfterWithGeneratorInvariantsNoError(foo
)
267 self
.assertAfterWithManagerInvariantsNoError(mock
)
269 def testNestedSingleStatements(self
):
270 mock_a
= mock_contextmanager_generator()
272 mock_b
= mock_contextmanager_generator()
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
)
286 class NestedNonexceptionalTestCase(unittest
.TestCase
,
287 ContextmanagerAssertionMixin
):
288 def testSingleArgInlineGeneratorSyntax(self
):
289 with
Nested(mock_contextmanager_generator()):
292 def testSingleArgBoundToNonTuple(self
):
293 m
= mock_contextmanager_generator()
294 # This will bind all the arguments to nested() into a single list
296 with
Nested(m
) as foo
:
297 self
.assertInWithManagerInvariants(m
)
298 self
.assertAfterWithManagerInvariantsNoError(m
)
300 def testSingleArgBoundToSingleElementParenthesizedList(self
):
301 m
= mock_contextmanager_generator()
302 # This will bind all the arguments to nested() into a single list
304 with
Nested(m
) as (foo
):
305 self
.assertInWithManagerInvariants(m
)
306 self
.assertAfterWithManagerInvariantsNoError(m
)
308 def testSingleArgBoundToMultipleElementTupleError(self
):
309 def shouldThrowValueError():
310 with
Nested(mock_contextmanager_generator()) as (foo
, bar
):
312 self
.assertRaises(ValueError, shouldThrowValueError
)
314 def testSingleArgUnbound(self
):
315 mock_contextmanager
= mock_contextmanager_generator()
316 mock_nested
= MockNested(mock_contextmanager
)
318 self
.assertInWithManagerInvariants(mock_contextmanager
)
319 self
.assertInWithManagerInvariants(mock_nested
)
320 self
.assertAfterWithManagerInvariantsNoError(mock_contextmanager
)
321 self
.assertAfterWithManagerInvariantsNoError(mock_nested
)
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
)
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
)
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
)
352 class ExceptionalTestCase(unittest
.TestCase
, ContextmanagerAssertionMixin
):
353 def testSingleResource(self
):
354 cm
= mock_contextmanager_generator()
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
)
364 def testExceptionNormalized(self
):
365 cm
= mock_contextmanager_generator()
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.
371 self
.assertRaises(ZeroDivisionError, shouldThrow
)
372 self
.assertAfterWithManagerInvariantsWithError(cm
, ZeroDivisionError)
374 def testNestedSingleStatements(self
):
375 mock_a
= mock_contextmanager_generator()
376 mock_b
= mock_contextmanager_generator()
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
)
391 def testMultipleResourcesInSingleStatement(self
):
392 cm_a
= mock_contextmanager_generator()
393 cm_b
= mock_contextmanager_generator()
394 mock_nested
= MockNested(cm_a
, cm_b
)
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
)
410 def testNestedExceptionBeforeInnerStatement(self
):
411 mock_a
= mock_contextmanager_generator()
412 mock_b
= mock_contextmanager_generator()
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
:
421 self
.assertRaises(RuntimeError, shouldThrow
)
422 self
.assertAfterWithManagerInvariantsWithError(mock_a
)
423 self
.assertAfterWithGeneratorInvariantsWithError(self
.foo
)
425 # The inner statement stuff should never have been touched
426 self
.assertEqual(self
.bar
, None)
427 self
.assertFalse(mock_b
.enter_called
)
428 self
.assertFalse(mock_b
.exit_called
)
429 self
.assertEqual(mock_b
.exit_args
, None)
431 def testNestedExceptionAfterInnerStatement(self
):
432 mock_a
= mock_contextmanager_generator()
433 mock_b
= mock_contextmanager_generator()
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
)
448 def testRaisedStopIteration1(self
):
456 raise StopIteration("from with")
458 self
.assertRaises(StopIteration, shouldThrow
)
460 def testRaisedStopIteration2(self
):
465 def __exit__(self
, type, value
, traceback
):
470 raise StopIteration("from with")
472 self
.assertRaises(StopIteration, shouldThrow
)
474 def testRaisedStopIteration3(self
):
475 # Another variant where the exception hasn't been instantiated
483 raise iter([]).next()
485 self
.assertRaises(StopIteration, shouldThrow
)
487 def testRaisedGeneratorExit1(self
):
495 raise GeneratorExit("from with")
497 self
.assertRaises(GeneratorExit
, shouldThrow
)
499 def testRaisedGeneratorExit2(self
):
504 def __exit__(self
, type, value
, traceback
):
509 raise GeneratorExit("from with")
511 self
.assertRaises(GeneratorExit
, shouldThrow
)
513 def testErrorsInBool(self
):
514 # issue4589: __exit__ return code may raise an exception
515 # when looking at its truth value.
518 def __init__(self
, bool_conversion
):
520 def __nonzero__(self
):
521 return bool_conversion()
522 self
.exit_result
= Bool()
525 def __exit__(self
, a
, b
, c
):
526 return self
.exit_result
529 with
cm(lambda: True):
530 self
.fail("Should NOT see this")
534 with
cm(lambda: False):
535 self
.fail("Should raise")
536 self
.assertRaises(AssertionError, falseAsBool
)
539 with
cm(lambda: 1 // 0):
540 self
.fail("Should NOT see this")
541 self
.assertRaises(ZeroDivisionError, failAsBool
)
544 class NonLocalFlowControlTestCase(unittest
.TestCase
):
546 def testWithBreak(self
):
550 with
mock_contextmanager_generator():
553 counter
+= 100 # Not reached
554 self
.assertEqual(counter
, 11)
556 def testWithContinue(self
):
562 with
mock_contextmanager_generator():
565 counter
+= 100 # Not reached
566 self
.assertEqual(counter
, 12)
568 def testWithReturn(self
):
573 with
mock_contextmanager_generator():
576 counter
+= 100 # Not reached
577 self
.assertEqual(foo(), 11)
579 def testWithYield(self
):
581 with
mock_contextmanager_generator():
585 self
.assertEqual(x
, [12, 13])
587 def testWithRaise(self
):
591 with
mock_contextmanager_generator():
594 counter
+= 100 # Not reached
596 self
.assertEqual(counter
, 11)
598 self
.fail("Didn't raise RuntimeError")
601 class AssignmentTargetTestCase(unittest
.TestCase
):
603 def testSingleComplexTarget(self
):
604 targets
= {1: [0, 1, 2]}
605 with
mock_contextmanager_generator() as targets
[1][0]:
606 self
.assertEqual(targets
.keys(), [1])
607 self
.assertEqual(targets
[1][0].__class
__, MockResource
)
608 with
mock_contextmanager_generator() as targets
.values()[0][1]:
609 self
.assertEqual(targets
.keys(), [1])
610 self
.assertEqual(targets
[1][1].__class
__, MockResource
)
611 with
mock_contextmanager_generator() as targets
[2]:
612 keys
= targets
.keys()
614 self
.assertEqual(keys
, [1, 2])
617 with
mock_contextmanager_generator() as blah
.foo
:
618 self
.assertEqual(hasattr(blah
, "foo"), True)
620 def testMultipleComplexTargets(self
):
622 def __enter__(self
): return 1, 2, 3
623 def __exit__(self
, t
, v
, tb
): pass
624 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]})
627 with
C() as (targets
.values()[0][2], targets
.values()[0][1], targets
.values()[0][0]):
628 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})
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)
639 class ExitSwallowsExceptionTestCase(unittest
.TestCase
):
641 def testExitTrueSwallowsException(self
):
642 class AfricanSwallow
:
643 def __enter__(self
): pass
644 def __exit__(self
, t
, v
, tb
): return True
646 with
AfricanSwallow():
648 except ZeroDivisionError:
649 self
.fail("ZeroDivisionError should have been swallowed")
651 def testExitFalseDoesntSwallowException(self
):
652 class EuropeanSwallow
:
653 def __enter__(self
): pass
654 def __exit__(self
, t
, v
, tb
): return False
656 with
EuropeanSwallow():
658 except ZeroDivisionError:
661 self
.fail("ZeroDivisionError should have been raised")
664 class NestedWith(unittest
.TestCase
):
667 def __init__(self
, value
=None, gobble
=False):
672 self
.enter_called
= False
673 self
.exit_called
= False
676 self
.enter_called
= True
679 def __exit__(self
, *exc_info
):
680 self
.exit_called
= True
681 self
.exc_info
= exc_info
685 class InitRaises(object):
686 def __init__(self
): raise RuntimeError()
688 class EnterRaises(object):
689 def __enter__(self
): raise RuntimeError()
690 def __exit__(self
, *exc_info
): pass
692 class ExitRaises(object):
693 def __enter__(self
): pass
694 def __exit__(self
, *exc_info
): raise RuntimeError()
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
)
703 def testExceptionInExprList(self
):
705 with self
.Dummy() as a
, self
.InitRaises():
709 self
.assertTrue(a
.enter_called
)
710 self
.assertTrue(a
.exit_called
)
712 def testExceptionInEnter(self
):
714 with self
.Dummy() as a
, self
.EnterRaises():
715 self
.fail('body of bad with executed')
719 self
.fail('RuntimeError not reraised')
720 self
.assertTrue(a
.enter_called
)
721 self
.assertTrue(a
.exit_called
)
723 def testExceptionInExit(self
):
724 body_executed
= False
725 with self
.Dummy(gobble
=True) as a
, self
.ExitRaises():
727 self
.assertTrue(a
.enter_called
)
728 self
.assertTrue(a
.exit_called
)
729 self
.assertTrue(body_executed
)
730 self
.assertNotEqual(a
.exc_info
[0], None)
732 def testEnterReturnsTuple(self
):
733 with self
.Dummy(value
=(1,2)) as (a1
, a2
), \
734 self
.Dummy(value
=(10, 20)) as (b1
, b2
):
735 self
.assertEquals(1, a1
)
736 self
.assertEquals(2, a2
)
737 self
.assertEquals(10, b1
)
738 self
.assertEquals(20, b2
)
741 run_unittest(FailureTestCase
, NonexceptionalTestCase
,
742 NestedNonexceptionalTestCase
, ExceptionalTestCase
,
743 NonLocalFlowControlTestCase
,
744 AssignmentTargetTestCase
,
745 ExitSwallowsExceptionTestCase
,
749 if __name__
== '__main__':