| #!/usr/bin/env python | |
| """Unit tests for the with statement specified in PEP 343.""" | |
| __author__ = "Mike Bland" | |
| __email__ = "mbland at acm dot org" | |
| import sys | |
| import unittest | |
| from collections import deque | |
| from contextlib import GeneratorContextManager, contextmanager | |
| from test.test_support import run_unittest | |
| class MockContextManager(GeneratorContextManager): | |
| def __init__(self, gen): | |
| GeneratorContextManager.__init__(self, gen) | |
| self.enter_called = False | |
| self.exit_called = False | |
| self.exit_args = None | |
| def __enter__(self): | |
| self.enter_called = True | |
| return GeneratorContextManager.__enter__(self) | |
| def __exit__(self, type, value, traceback): | |
| self.exit_called = True | |
| self.exit_args = (type, value, traceback) | |
| return GeneratorContextManager.__exit__(self, type, | |
| value, traceback) | |
| def mock_contextmanager(func): | |
| def helper(*args, **kwds): | |
| return MockContextManager(func(*args, **kwds)) | |
| return helper | |
| class MockResource(object): | |
| def __init__(self): | |
| self.yielded = False | |
| self.stopped = False | |
| @mock_contextmanager | |
| def mock_contextmanager_generator(): | |
| mock = MockResource() | |
| try: | |
| mock.yielded = True | |
| yield mock | |
| finally: | |
| mock.stopped = True | |
| class Nested(object): | |
| def __init__(self, *managers): | |
| self.managers = managers | |
| self.entered = None | |
| def __enter__(self): | |
| if self.entered is not None: | |
| raise RuntimeError("Context is not reentrant") | |
| self.entered = deque() | |
| vars = [] | |
| try: | |
| for mgr in self.managers: | |
| vars.append(mgr.__enter__()) | |
| self.entered.appendleft(mgr) | |
| except: | |
| if not self.__exit__(*sys.exc_info()): | |
| raise | |
| return vars | |
| def __exit__(self, *exc_info): | |
| # Behave like nested with statements | |
| # first in, last out | |
| # New exceptions override old ones | |
| ex = exc_info | |
| for mgr in self.entered: | |
| try: | |
| if mgr.__exit__(*ex): | |
| ex = (None, None, None) | |
| except: | |
| ex = sys.exc_info() | |
| self.entered = None | |
| if ex is not exc_info: | |
| raise ex[0], ex[1], ex[2] | |
| class MockNested(Nested): | |
| def __init__(self, *managers): | |
| Nested.__init__(self, *managers) | |
| self.enter_called = False | |
| self.exit_called = False | |
| self.exit_args = None | |
| def __enter__(self): | |
| self.enter_called = True | |
| return Nested.__enter__(self) | |
| def __exit__(self, *exc_info): | |
| self.exit_called = True | |
| self.exit_args = exc_info | |
| return Nested.__exit__(self, *exc_info) | |
| class FailureTestCase(unittest.TestCase): | |
| def testNameError(self): | |
| def fooNotDeclared(): | |
| with foo: pass | |
| self.assertRaises(NameError, fooNotDeclared) | |
| def testEnterAttributeError(self): | |
| class LacksEnter(object): | |
| def __exit__(self, type, value, traceback): | |
| pass | |
| def fooLacksEnter(): | |
| foo = LacksEnter() | |
| with foo: pass | |
| self.assertRaises(AttributeError, fooLacksEnter) | |
| def testExitAttributeError(self): | |
| class LacksExit(object): | |
| def __enter__(self): | |
| pass | |
| def fooLacksExit(): | |
| foo = LacksExit() | |
| with foo: pass | |
| self.assertRaises(AttributeError, fooLacksExit) | |
| def assertRaisesSyntaxError(self, codestr): | |
| def shouldRaiseSyntaxError(s): | |
| compile(s, '', 'single') | |
| self.assertRaises(SyntaxError, shouldRaiseSyntaxError, codestr) | |
| def testAssignmentToNoneError(self): | |
| self.assertRaisesSyntaxError('with mock as None:\n pass') | |
| self.assertRaisesSyntaxError( | |
| 'with mock as (None):\n' | |
| ' pass') | |
| def testAssignmentToEmptyTupleError(self): | |
| self.assertRaisesSyntaxError( | |
| 'with mock as ():\n' | |
| ' pass') | |
| def testAssignmentToTupleOnlyContainingNoneError(self): | |
| self.assertRaisesSyntaxError('with mock as None,:\n pass') | |
| self.assertRaisesSyntaxError( | |
| 'with mock as (None,):\n' | |
| ' pass') | |
| def testAssignmentToTupleContainingNoneError(self): | |
| self.assertRaisesSyntaxError( | |
| 'with mock as (foo, None, bar):\n' | |
| ' pass') | |
| def testEnterThrows(self): | |
| class EnterThrows(object): | |
| def __enter__(self): | |
| raise RuntimeError("Enter threw") | |
| def __exit__(self, *args): | |
| pass | |
| def shouldThrow(): | |
| ct = EnterThrows() | |
| self.foo = None | |
| with ct as self.foo: | |
| pass | |
| self.assertRaises(RuntimeError, shouldThrow) | |
| self.assertEqual(self.foo, None) | |
| def testExitThrows(self): | |
| class ExitThrows(object): | |
| def __enter__(self): | |
| return | |
| def __exit__(self, *args): | |
| raise RuntimeError(42) | |
| def shouldThrow(): | |
| with ExitThrows(): | |
| pass | |
| self.assertRaises(RuntimeError, shouldThrow) | |
| class ContextmanagerAssertionMixin(object): | |
| TEST_EXCEPTION = RuntimeError("test exception") | |
| def assertInWithManagerInvariants(self, mock_manager): | |
| self.assertTrue(mock_manager.enter_called) | |
| self.assertFalse(mock_manager.exit_called) | |
| self.assertEqual(mock_manager.exit_args, None) | |
| def assertAfterWithManagerInvariants(self, mock_manager, exit_args): | |
| self.assertTrue(mock_manager.enter_called) | |
| self.assertTrue(mock_manager.exit_called) | |
| self.assertEqual(mock_manager.exit_args, exit_args) | |
| def assertAfterWithManagerInvariantsNoError(self, mock_manager): | |
| self.assertAfterWithManagerInvariants(mock_manager, | |
| (None, None, None)) | |
| def assertInWithGeneratorInvariants(self, mock_generator): | |
| self.assertTrue(mock_generator.yielded) | |
| self.assertFalse(mock_generator.stopped) | |
| def assertAfterWithGeneratorInvariantsNoError(self, mock_generator): | |
| self.assertTrue(mock_generator.yielded) | |
| self.assertTrue(mock_generator.stopped) | |
| def raiseTestException(self): | |
| raise self.TEST_EXCEPTION | |
| def assertAfterWithManagerInvariantsWithError(self, mock_manager, | |
| exc_type=None): | |
| self.assertTrue(mock_manager.enter_called) | |
| self.assertTrue(mock_manager.exit_called) | |
| if exc_type is None: | |
| self.assertEqual(mock_manager.exit_args[1], self.TEST_EXCEPTION) | |
| exc_type = type(self.TEST_EXCEPTION) | |
| self.assertEqual(mock_manager.exit_args[0], exc_type) | |
| # Test the __exit__ arguments. Issue #7853 | |
| self.assertIsInstance(mock_manager.exit_args[1], exc_type) | |
| self.assertIsNot(mock_manager.exit_args[2], None) | |
| def assertAfterWithGeneratorInvariantsWithError(self, mock_generator): | |
| self.assertTrue(mock_generator.yielded) | |
| self.assertTrue(mock_generator.stopped) | |
| class NonexceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin): | |
| def testInlineGeneratorSyntax(self): | |
| with mock_contextmanager_generator(): | |
| pass | |
| def testUnboundGenerator(self): | |
| mock = mock_contextmanager_generator() | |
| with mock: | |
| pass | |
| self.assertAfterWithManagerInvariantsNoError(mock) | |
| def testInlineGeneratorBoundSyntax(self): | |
| with mock_contextmanager_generator() as foo: | |
| self.assertInWithGeneratorInvariants(foo) | |
| # FIXME: In the future, we'll try to keep the bound names from leaking | |
| self.assertAfterWithGeneratorInvariantsNoError(foo) | |
| def testInlineGeneratorBoundToExistingVariable(self): | |
| foo = None | |
| with mock_contextmanager_generator() as foo: | |
| self.assertInWithGeneratorInvariants(foo) | |
| self.assertAfterWithGeneratorInvariantsNoError(foo) | |
| def testInlineGeneratorBoundToDottedVariable(self): | |
| with mock_contextmanager_generator() as self.foo: | |
| self.assertInWithGeneratorInvariants(self.foo) | |
| self.assertAfterWithGeneratorInvariantsNoError(self.foo) | |
| def testBoundGenerator(self): | |
| mock = mock_contextmanager_generator() | |
| with mock as foo: | |
| self.assertInWithGeneratorInvariants(foo) | |
| self.assertInWithManagerInvariants(mock) | |
| self.assertAfterWithGeneratorInvariantsNoError(foo) | |
| self.assertAfterWithManagerInvariantsNoError(mock) | |
| def testNestedSingleStatements(self): | |
| mock_a = mock_contextmanager_generator() | |
| with mock_a as foo: | |
| mock_b = mock_contextmanager_generator() | |
| with mock_b as bar: | |
| self.assertInWithManagerInvariants(mock_a) | |
| self.assertInWithManagerInvariants(mock_b) | |
| self.assertInWithGeneratorInvariants(foo) | |
| self.assertInWithGeneratorInvariants(bar) | |
| self.assertAfterWithManagerInvariantsNoError(mock_b) | |
| self.assertAfterWithGeneratorInvariantsNoError(bar) | |
| self.assertInWithManagerInvariants(mock_a) | |
| self.assertInWithGeneratorInvariants(foo) | |
| self.assertAfterWithManagerInvariantsNoError(mock_a) | |
| self.assertAfterWithGeneratorInvariantsNoError(foo) | |
| class NestedNonexceptionalTestCase(unittest.TestCase, | |
| ContextmanagerAssertionMixin): | |
| def testSingleArgInlineGeneratorSyntax(self): | |
| with Nested(mock_contextmanager_generator()): | |
| pass | |
| def testSingleArgBoundToNonTuple(self): | |
| m = mock_contextmanager_generator() | |
| # This will bind all the arguments to nested() into a single list | |
| # assigned to foo. | |
| with Nested(m) as foo: | |
| self.assertInWithManagerInvariants(m) | |
| self.assertAfterWithManagerInvariantsNoError(m) | |
| def testSingleArgBoundToSingleElementParenthesizedList(self): | |
| m = mock_contextmanager_generator() | |
| # This will bind all the arguments to nested() into a single list | |
| # assigned to foo. | |
| with Nested(m) as (foo): | |
| self.assertInWithManagerInvariants(m) | |
| self.assertAfterWithManagerInvariantsNoError(m) | |
| def testSingleArgBoundToMultipleElementTupleError(self): | |
| def shouldThrowValueError(): | |
| with Nested(mock_contextmanager_generator()) as (foo, bar): | |
| pass | |
| self.assertRaises(ValueError, shouldThrowValueError) | |
| def testSingleArgUnbound(self): | |
| mock_contextmanager = mock_contextmanager_generator() | |
| mock_nested = MockNested(mock_contextmanager) | |
| with mock_nested: | |
| self.assertInWithManagerInvariants(mock_contextmanager) | |
| self.assertInWithManagerInvariants(mock_nested) | |
| self.assertAfterWithManagerInvariantsNoError(mock_contextmanager) | |
| self.assertAfterWithManagerInvariantsNoError(mock_nested) | |
| def testMultipleArgUnbound(self): | |
| m = mock_contextmanager_generator() | |
| n = mock_contextmanager_generator() | |
| o = mock_contextmanager_generator() | |
| mock_nested = MockNested(m, n, o) | |
| with mock_nested: | |
| self.assertInWithManagerInvariants(m) | |
| self.assertInWithManagerInvariants(n) | |
| self.assertInWithManagerInvariants(o) | |
| self.assertInWithManagerInvariants(mock_nested) | |
| self.assertAfterWithManagerInvariantsNoError(m) | |
| self.assertAfterWithManagerInvariantsNoError(n) | |
| self.assertAfterWithManagerInvariantsNoError(o) | |
| self.assertAfterWithManagerInvariantsNoError(mock_nested) | |
| def testMultipleArgBound(self): | |
| mock_nested = MockNested(mock_contextmanager_generator(), | |
| mock_contextmanager_generator(), mock_contextmanager_generator()) | |
| with mock_nested as (m, n, o): | |
| self.assertInWithGeneratorInvariants(m) | |
| self.assertInWithGeneratorInvariants(n) | |
| self.assertInWithGeneratorInvariants(o) | |
| self.assertInWithManagerInvariants(mock_nested) | |
| self.assertAfterWithGeneratorInvariantsNoError(m) | |
| self.assertAfterWithGeneratorInvariantsNoError(n) | |
| self.assertAfterWithGeneratorInvariantsNoError(o) | |
| self.assertAfterWithManagerInvariantsNoError(mock_nested) | |
| class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin): | |
| def testSingleResource(self): | |
| cm = mock_contextmanager_generator() | |
| def shouldThrow(): | |
| with cm as self.resource: | |
| self.assertInWithManagerInvariants(cm) | |
| self.assertInWithGeneratorInvariants(self.resource) | |
| self.raiseTestException() | |
| self.assertRaises(RuntimeError, shouldThrow) | |
| self.assertAfterWithManagerInvariantsWithError(cm) | |
| self.assertAfterWithGeneratorInvariantsWithError(self.resource) | |
| def testExceptionNormalized(self): | |
| cm = mock_contextmanager_generator() | |
| def shouldThrow(): | |
| with cm as self.resource: | |
| # Note this relies on the fact that 1 // 0 produces an exception | |
| # that is not normalized immediately. | |
| 1 // 0 | |
| self.assertRaises(ZeroDivisionError, shouldThrow) | |
| self.assertAfterWithManagerInvariantsWithError(cm, ZeroDivisionError) | |
| def testNestedSingleStatements(self): | |
| mock_a = mock_contextmanager_generator() | |
| mock_b = mock_contextmanager_generator() | |
| def shouldThrow(): | |
| with mock_a as self.foo: | |
| with mock_b as self.bar: | |
| self.assertInWithManagerInvariants(mock_a) | |
| self.assertInWithManagerInvariants(mock_b) | |
| self.assertInWithGeneratorInvariants(self.foo) | |
| self.assertInWithGeneratorInvariants(self.bar) | |
| self.raiseTestException() | |
| self.assertRaises(RuntimeError, shouldThrow) | |
| self.assertAfterWithManagerInvariantsWithError(mock_a) | |
| self.assertAfterWithManagerInvariantsWithError(mock_b) | |
| self.assertAfterWithGeneratorInvariantsWithError(self.foo) | |
| self.assertAfterWithGeneratorInvariantsWithError(self.bar) | |
| def testMultipleResourcesInSingleStatement(self): | |
| cm_a = mock_contextmanager_generator() | |
| cm_b = mock_contextmanager_generator() | |
| mock_nested = MockNested(cm_a, cm_b) | |
| def shouldThrow(): | |
| with mock_nested as (self.resource_a, self.resource_b): | |
| self.assertInWithManagerInvariants(cm_a) | |
| self.assertInWithManagerInvariants(cm_b) | |
| self.assertInWithManagerInvariants(mock_nested) | |
| self.assertInWithGeneratorInvariants(self.resource_a) | |
| self.assertInWithGeneratorInvariants(self.resource_b) | |
| self.raiseTestException() | |
| self.assertRaises(RuntimeError, shouldThrow) | |
| self.assertAfterWithManagerInvariantsWithError(cm_a) | |
| self.assertAfterWithManagerInvariantsWithError(cm_b) | |
| self.assertAfterWithManagerInvariantsWithError(mock_nested) | |
| self.assertAfterWithGeneratorInvariantsWithError(self.resource_a) | |
| self.assertAfterWithGeneratorInvariantsWithError(self.resource_b) | |
| def testNestedExceptionBeforeInnerStatement(self): | |
| mock_a = mock_contextmanager_generator() | |
| mock_b = mock_contextmanager_generator() | |
| self.bar = None | |
| def shouldThrow(): | |
| with mock_a as self.foo: | |
| self.assertInWithManagerInvariants(mock_a) | |
| self.assertInWithGeneratorInvariants(self.foo) | |
| self.raiseTestException() | |
| with mock_b as self.bar: | |
| pass | |
| self.assertRaises(RuntimeError, shouldThrow) | |
| self.assertAfterWithManagerInvariantsWithError(mock_a) | |
| self.assertAfterWithGeneratorInvariantsWithError(self.foo) | |
| # The inner statement stuff should never have been touched | |
| self.assertEqual(self.bar, None) | |
| self.assertFalse(mock_b.enter_called) | |
| self.assertFalse(mock_b.exit_called) | |
| self.assertEqual(mock_b.exit_args, None) | |
| def testNestedExceptionAfterInnerStatement(self): | |
| mock_a = mock_contextmanager_generator() | |
| mock_b = mock_contextmanager_generator() | |
| def shouldThrow(): | |
| with mock_a as self.foo: | |
| with mock_b as self.bar: | |
| self.assertInWithManagerInvariants(mock_a) | |
| self.assertInWithManagerInvariants(mock_b) | |
| self.assertInWithGeneratorInvariants(self.foo) | |
| self.assertInWithGeneratorInvariants(self.bar) | |
| self.raiseTestException() | |
| self.assertRaises(RuntimeError, shouldThrow) | |
| self.assertAfterWithManagerInvariantsWithError(mock_a) | |
| self.assertAfterWithManagerInvariantsNoError(mock_b) | |
| self.assertAfterWithGeneratorInvariantsWithError(self.foo) | |
| self.assertAfterWithGeneratorInvariantsNoError(self.bar) | |
| def testRaisedStopIteration1(self): | |
| # From bug 1462485 | |
| @contextmanager | |
| def cm(): | |
| yield | |
| def shouldThrow(): | |
| with cm(): | |
| raise StopIteration("from with") | |
| self.assertRaises(StopIteration, shouldThrow) | |
| def testRaisedStopIteration2(self): | |
| # From bug 1462485 | |
| class cm(object): | |
| def __enter__(self): | |
| pass | |
| def __exit__(self, type, value, traceback): | |
| pass | |
| def shouldThrow(): | |
| with cm(): | |
| raise StopIteration("from with") | |
| self.assertRaises(StopIteration, shouldThrow) | |
| def testRaisedStopIteration3(self): | |
| # Another variant where the exception hasn't been instantiated | |
| # From bug 1705170 | |
| @contextmanager | |
| def cm(): | |
| yield | |
| def shouldThrow(): | |
| with cm(): | |
| raise iter([]).next() | |
| self.assertRaises(StopIteration, shouldThrow) | |
| def testRaisedGeneratorExit1(self): | |
| # From bug 1462485 | |
| @contextmanager | |
| def cm(): | |
| yield | |
| def shouldThrow(): | |
| with cm(): | |
| raise GeneratorExit("from with") | |
| self.assertRaises(GeneratorExit, shouldThrow) | |
| def testRaisedGeneratorExit2(self): | |
| # From bug 1462485 | |
| class cm (object): | |
| def __enter__(self): | |
| pass | |
| def __exit__(self, type, value, traceback): | |
| pass | |
| def shouldThrow(): | |
| with cm(): | |
| raise GeneratorExit("from with") | |
| self.assertRaises(GeneratorExit, shouldThrow) | |
| def testErrorsInBool(self): | |
| # issue4589: __exit__ return code may raise an exception | |
| # when looking at its truth value. | |
| class cm(object): | |
| def __init__(self, bool_conversion): | |
| class Bool: | |
| def __nonzero__(self): | |
| return bool_conversion() | |
| self.exit_result = Bool() | |
| def __enter__(self): | |
| return 3 | |
| def __exit__(self, a, b, c): | |
| return self.exit_result | |
| def trueAsBool(): | |
| with cm(lambda: True): | |
| self.fail("Should NOT see this") | |
| trueAsBool() | |
| def falseAsBool(): | |
| with cm(lambda: False): | |
| self.fail("Should raise") | |
| self.assertRaises(AssertionError, falseAsBool) | |
| def failAsBool(): | |
| with cm(lambda: 1 // 0): | |
| self.fail("Should NOT see this") | |
| self.assertRaises(ZeroDivisionError, failAsBool) | |
| class NonLocalFlowControlTestCase(unittest.TestCase): | |
| def testWithBreak(self): | |
| counter = 0 | |
| while True: | |
| counter += 1 | |
| with mock_contextmanager_generator(): | |
| counter += 10 | |
| break | |
| counter += 100 # Not reached | |
| self.assertEqual(counter, 11) | |
| def testWithContinue(self): | |
| counter = 0 | |
| while True: | |
| counter += 1 | |
| if counter > 2: | |
| break | |
| with mock_contextmanager_generator(): | |
| counter += 10 | |
| continue | |
| counter += 100 # Not reached | |
| self.assertEqual(counter, 12) | |
| def testWithReturn(self): | |
| def foo(): | |
| counter = 0 | |
| while True: | |
| counter += 1 | |
| with mock_contextmanager_generator(): | |
| counter += 10 | |
| return counter | |
| counter += 100 # Not reached | |
| self.assertEqual(foo(), 11) | |
| def testWithYield(self): | |
| def gen(): | |
| with mock_contextmanager_generator(): | |
| yield 12 | |
| yield 13 | |
| x = list(gen()) | |
| self.assertEqual(x, [12, 13]) | |
| def testWithRaise(self): | |
| counter = 0 | |
| try: | |
| counter += 1 | |
| with mock_contextmanager_generator(): | |
| counter += 10 | |
| raise RuntimeError | |
| counter += 100 # Not reached | |
| except RuntimeError: | |
| self.assertEqual(counter, 11) | |
| else: | |
| self.fail("Didn't raise RuntimeError") | |
| class AssignmentTargetTestCase(unittest.TestCase): | |
| def testSingleComplexTarget(self): | |
| targets = {1: [0, 1, 2]} | |
| with mock_contextmanager_generator() as targets[1][0]: | |
| self.assertEqual(targets.keys(), [1]) | |
| self.assertEqual(targets[1][0].__class__, MockResource) | |
| with mock_contextmanager_generator() as targets.values()[0][1]: | |
| self.assertEqual(targets.keys(), [1]) | |
| self.assertEqual(targets[1][1].__class__, MockResource) | |
| with mock_contextmanager_generator() as targets[2]: | |
| keys = targets.keys() | |
| keys.sort() | |
| self.assertEqual(keys, [1, 2]) | |
| class C: pass | |
| blah = C() | |
| with mock_contextmanager_generator() as blah.foo: | |
| self.assertEqual(hasattr(blah, "foo"), True) | |
| def testMultipleComplexTargets(self): | |
| class C: | |
| def __enter__(self): return 1, 2, 3 | |
| def __exit__(self, t, v, tb): pass | |
| targets = {1: [0, 1, 2]} | |
| with C() as (targets[1][0], targets[1][1], targets[1][2]): | |
| self.assertEqual(targets, {1: [1, 2, 3]}) | |
| with C() as (targets.values()[0][2], targets.values()[0][1], targets.values()[0][0]): | |
| self.assertEqual(targets, {1: [3, 2, 1]}) | |
| with C() as (targets[1], targets[2], targets[3]): | |
| self.assertEqual(targets, {1: 1, 2: 2, 3: 3}) | |
| class B: pass | |
| blah = B() | |
| with C() as (blah.one, blah.two, blah.three): | |
| self.assertEqual(blah.one, 1) | |
| self.assertEqual(blah.two, 2) | |
| self.assertEqual(blah.three, 3) | |
| class ExitSwallowsExceptionTestCase(unittest.TestCase): | |
| def testExitTrueSwallowsException(self): | |
| class AfricanSwallow: | |
| def __enter__(self): pass | |
| def __exit__(self, t, v, tb): return True | |
| try: | |
| with AfricanSwallow(): | |
| 1 // 0 | |
| except ZeroDivisionError: | |
| self.fail("ZeroDivisionError should have been swallowed") | |
| def testExitFalseDoesntSwallowException(self): | |
| class EuropeanSwallow: | |
| def __enter__(self): pass | |
| def __exit__(self, t, v, tb): return False | |
| try: | |
| with EuropeanSwallow(): | |
| 1 // 0 | |
| except ZeroDivisionError: | |
| pass | |
| else: | |
| self.fail("ZeroDivisionError should have been raised") | |
| class NestedWith(unittest.TestCase): | |
| class Dummy(object): | |
| def __init__(self, value=None, gobble=False): | |
| if value is None: | |
| value = self | |
| self.value = value | |
| self.gobble = gobble | |
| self.enter_called = False | |
| self.exit_called = False | |
| def __enter__(self): | |
| self.enter_called = True | |
| return self.value | |
| def __exit__(self, *exc_info): | |
| self.exit_called = True | |
| self.exc_info = exc_info | |
| if self.gobble: | |
| return True | |
| class InitRaises(object): | |
| def __init__(self): raise RuntimeError() | |
| class EnterRaises(object): | |
| def __enter__(self): raise RuntimeError() | |
| def __exit__(self, *exc_info): pass | |
| class ExitRaises(object): | |
| def __enter__(self): pass | |
| def __exit__(self, *exc_info): raise RuntimeError() | |
| def testNoExceptions(self): | |
| with self.Dummy() as a, self.Dummy() as b: | |
| self.assertTrue(a.enter_called) | |
| self.assertTrue(b.enter_called) | |
| self.assertTrue(a.exit_called) | |
| self.assertTrue(b.exit_called) | |
| def testExceptionInExprList(self): | |
| try: | |
| with self.Dummy() as a, self.InitRaises(): | |
| pass | |
| except: | |
| pass | |
| self.assertTrue(a.enter_called) | |
| self.assertTrue(a.exit_called) | |
| def testExceptionInEnter(self): | |
| try: | |
| with self.Dummy() as a, self.EnterRaises(): | |
| self.fail('body of bad with executed') | |
| except RuntimeError: | |
| pass | |
| else: | |
| self.fail('RuntimeError not reraised') | |
| self.assertTrue(a.enter_called) | |
| self.assertTrue(a.exit_called) | |
| def testExceptionInExit(self): | |
| body_executed = False | |
| with self.Dummy(gobble=True) as a, self.ExitRaises(): | |
| body_executed = True | |
| self.assertTrue(a.enter_called) | |
| self.assertTrue(a.exit_called) | |
| self.assertTrue(body_executed) | |
| self.assertNotEqual(a.exc_info[0], None) | |
| def testEnterReturnsTuple(self): | |
| with self.Dummy(value=(1,2)) as (a1, a2), \ | |
| self.Dummy(value=(10, 20)) as (b1, b2): | |
| self.assertEqual(1, a1) | |
| self.assertEqual(2, a2) | |
| self.assertEqual(10, b1) | |
| self.assertEqual(20, b2) | |
| def test_main(): | |
| run_unittest(FailureTestCase, NonexceptionalTestCase, | |
| NestedNonexceptionalTestCase, ExceptionalTestCase, | |
| NonLocalFlowControlTestCase, | |
| AssignmentTargetTestCase, | |
| ExitSwallowsExceptionTestCase, | |
| NestedWith) | |
| if __name__ == '__main__': | |
| test_main() |