Home | History | Annotate | Download | only in test
      1 """Unit tests for the with statement specified in PEP 343."""
      2 
      3 
      4 __author__ = "Mike Bland"
      5 __email__ = "mbland at acm dot org"
      6 
      7 import sys
      8 import unittest
      9 from collections import deque
     10 from contextlib import _GeneratorContextManager, contextmanager
     11 
     12 
     13 class MockContextManager(_GeneratorContextManager):
     14     def __init__(self, *args):
     15         super().__init__(*args)
     16         self.enter_called = False
     17         self.exit_called = False
     18         self.exit_args = None
     19 
     20     def __enter__(self):
     21         self.enter_called = True
     22         return _GeneratorContextManager.__enter__(self)
     23 
     24     def __exit__(self, type, value, traceback):
     25         self.exit_called = True
     26         self.exit_args = (type, value, traceback)
     27         return _GeneratorContextManager.__exit__(self, type,
     28                                                  value, traceback)
     29 
     30 
     31 def mock_contextmanager(func):
     32     def helper(*args, **kwds):
     33         return MockContextManager(func, args, kwds)
     34     return helper
     35 
     36 
     37 class MockResource(object):
     38     def __init__(self):
     39         self.yielded = False
     40         self.stopped = False
     41 
     42 
     43 @mock_contextmanager
     44 def mock_contextmanager_generator():
     45     mock = MockResource()
     46     try:
     47         mock.yielded = True
     48         yield mock
     49     finally:
     50         mock.stopped = True
     51 
     52 
     53 class Nested(object):
     54 
     55     def __init__(self, *managers):
     56         self.managers = managers
     57         self.entered = None
     58 
     59     def __enter__(self):
     60         if self.entered is not None:
     61             raise RuntimeError("Context is not reentrant")
     62         self.entered = deque()
     63         vars = []
     64         try:
     65             for mgr in self.managers:
     66                 vars.append(mgr.__enter__())
     67                 self.entered.appendleft(mgr)
     68         except:
     69             if not self.__exit__(*sys.exc_info()):
     70                 raise
     71         return vars
     72 
     73     def __exit__(self, *exc_info):
     74         # Behave like nested with statements
     75         # first in, last out
     76         # New exceptions override old ones
     77         ex = exc_info
     78         for mgr in self.entered:
     79             try:
     80                 if mgr.__exit__(*ex):
     81                     ex = (None, None, None)
     82             except:
     83                 ex = sys.exc_info()
     84         self.entered = None
     85         if ex is not exc_info:
     86             raise ex[0](ex[1]).with_traceback(ex[2])
     87 
     88 
     89 class MockNested(Nested):
     90     def __init__(self, *managers):
     91         Nested.__init__(self, *managers)
     92         self.enter_called = False
     93         self.exit_called = False
     94         self.exit_args = None
     95 
     96     def __enter__(self):
     97         self.enter_called = True
     98         return Nested.__enter__(self)
     99 
    100     def __exit__(self, *exc_info):
    101         self.exit_called = True
    102         self.exit_args = exc_info
    103         return Nested.__exit__(self, *exc_info)
    104 
    105 
    106 class FailureTestCase(unittest.TestCase):
    107     def testNameError(self):
    108         def fooNotDeclared():
    109             with foo: pass
    110         self.assertRaises(NameError, fooNotDeclared)
    111 
    112     def testEnterAttributeError1(self):
    113         class LacksEnter(object):
    114             def __exit__(self, type, value, traceback):
    115                 pass
    116 
    117         def fooLacksEnter():
    118             foo = LacksEnter()
    119             with foo: pass
    120         self.assertRaisesRegex(AttributeError, '__enter__', fooLacksEnter)
    121 
    122     def testEnterAttributeError2(self):
    123         class LacksEnterAndExit(object):
    124             pass
    125 
    126         def fooLacksEnterAndExit():
    127             foo = LacksEnterAndExit()
    128             with foo: pass
    129         self.assertRaisesRegex(AttributeError, '__enter__', fooLacksEnterAndExit)
    130 
    131     def testExitAttributeError(self):
    132         class LacksExit(object):
    133             def __enter__(self):
    134                 pass
    135 
    136         def fooLacksExit():
    137             foo = LacksExit()
    138             with foo: pass
    139         self.assertRaisesRegex(AttributeError, '__exit__', fooLacksExit)
    140 
    141     def assertRaisesSyntaxError(self, codestr):
    142         def shouldRaiseSyntaxError(s):
    143             compile(s, '', 'single')
    144         self.assertRaises(SyntaxError, shouldRaiseSyntaxError, codestr)
    145 
    146     def testAssignmentToNoneError(self):
    147         self.assertRaisesSyntaxError('with mock as None:\n  pass')
    148         self.assertRaisesSyntaxError(
    149             'with mock as (None):\n'
    150             '  pass')
    151 
    152     def testAssignmentToTupleOnlyContainingNoneError(self):
    153         self.assertRaisesSyntaxError('with mock as None,:\n  pass')
    154         self.assertRaisesSyntaxError(
    155             'with mock as (None,):\n'
    156             '  pass')
    157 
    158     def testAssignmentToTupleContainingNoneError(self):
    159         self.assertRaisesSyntaxError(
    160             'with mock as (foo, None, bar):\n'
    161             '  pass')
    162 
    163     def testEnterThrows(self):
    164         class EnterThrows(object):
    165             def __enter__(self):
    166                 raise RuntimeError("Enter threw")
    167             def __exit__(self, *args):
    168                 pass
    169 
    170         def shouldThrow():
    171             ct = EnterThrows()
    172             self.foo = None
    173             with ct as self.foo:
    174                 pass
    175         self.assertRaises(RuntimeError, shouldThrow)
    176         self.assertEqual(self.foo, None)
    177 
    178     def testExitThrows(self):
    179         class ExitThrows(object):
    180             def __enter__(self):
    181                 return
    182             def __exit__(self, *args):
    183                 raise RuntimeError(42)
    184         def shouldThrow():
    185             with ExitThrows():
    186                 pass
    187         self.assertRaises(RuntimeError, shouldThrow)
    188 
    189 class ContextmanagerAssertionMixin(object):
    190 
    191     def setUp(self):
    192         self.TEST_EXCEPTION = RuntimeError("test exception")
    193 
    194     def assertInWithManagerInvariants(self, mock_manager):
    195         self.assertTrue(mock_manager.enter_called)
    196         self.assertFalse(mock_manager.exit_called)
    197         self.assertEqual(mock_manager.exit_args, None)
    198 
    199     def assertAfterWithManagerInvariants(self, mock_manager, exit_args):
    200         self.assertTrue(mock_manager.enter_called)
    201         self.assertTrue(mock_manager.exit_called)
    202         self.assertEqual(mock_manager.exit_args, exit_args)
    203 
    204     def assertAfterWithManagerInvariantsNoError(self, mock_manager):
    205         self.assertAfterWithManagerInvariants(mock_manager,
    206             (None, None, None))
    207 
    208     def assertInWithGeneratorInvariants(self, mock_generator):
    209         self.assertTrue(mock_generator.yielded)
    210         self.assertFalse(mock_generator.stopped)
    211 
    212     def assertAfterWithGeneratorInvariantsNoError(self, mock_generator):
    213         self.assertTrue(mock_generator.yielded)
    214         self.assertTrue(mock_generator.stopped)
    215 
    216     def raiseTestException(self):
    217         raise self.TEST_EXCEPTION
    218 
    219     def assertAfterWithManagerInvariantsWithError(self, mock_manager,
    220                                                   exc_type=None):
    221         self.assertTrue(mock_manager.enter_called)
    222         self.assertTrue(mock_manager.exit_called)
    223         if exc_type is None:
    224             self.assertEqual(mock_manager.exit_args[1], self.TEST_EXCEPTION)
    225             exc_type = type(self.TEST_EXCEPTION)
    226         self.assertEqual(mock_manager.exit_args[0], exc_type)
    227         # Test the __exit__ arguments. Issue #7853
    228         self.assertIsInstance(mock_manager.exit_args[1], exc_type)
    229         self.assertIsNot(mock_manager.exit_args[2], None)
    230 
    231     def assertAfterWithGeneratorInvariantsWithError(self, mock_generator):
    232         self.assertTrue(mock_generator.yielded)
    233         self.assertTrue(mock_generator.stopped)
    234 
    235 
    236 class NonexceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
    237     def testInlineGeneratorSyntax(self):
    238         with mock_contextmanager_generator():
    239             pass
    240 
    241     def testUnboundGenerator(self):
    242         mock = mock_contextmanager_generator()
    243         with mock:
    244             pass
    245         self.assertAfterWithManagerInvariantsNoError(mock)
    246 
    247     def testInlineGeneratorBoundSyntax(self):
    248         with mock_contextmanager_generator() as foo:
    249             self.assertInWithGeneratorInvariants(foo)
    250         # FIXME: In the future, we'll try to keep the bound names from leaking
    251         self.assertAfterWithGeneratorInvariantsNoError(foo)
    252 
    253     def testInlineGeneratorBoundToExistingVariable(self):
    254         foo = None
    255         with mock_contextmanager_generator() as foo:
    256             self.assertInWithGeneratorInvariants(foo)
    257         self.assertAfterWithGeneratorInvariantsNoError(foo)
    258 
    259     def testInlineGeneratorBoundToDottedVariable(self):
    260         with mock_contextmanager_generator() as self.foo:
    261             self.assertInWithGeneratorInvariants(self.foo)
    262         self.assertAfterWithGeneratorInvariantsNoError(self.foo)
    263 
    264     def testBoundGenerator(self):
    265         mock = mock_contextmanager_generator()
    266         with mock as foo:
    267             self.assertInWithGeneratorInvariants(foo)
    268             self.assertInWithManagerInvariants(mock)
    269         self.assertAfterWithGeneratorInvariantsNoError(foo)
    270         self.assertAfterWithManagerInvariantsNoError(mock)
    271 
    272     def testNestedSingleStatements(self):
    273         mock_a = mock_contextmanager_generator()
    274         with mock_a as foo:
    275             mock_b = mock_contextmanager_generator()
    276             with mock_b as bar:
    277                 self.assertInWithManagerInvariants(mock_a)
    278                 self.assertInWithManagerInvariants(mock_b)
    279                 self.assertInWithGeneratorInvariants(foo)
    280                 self.assertInWithGeneratorInvariants(bar)
    281             self.assertAfterWithManagerInvariantsNoError(mock_b)
    282             self.assertAfterWithGeneratorInvariantsNoError(bar)
    283             self.assertInWithManagerInvariants(mock_a)
    284             self.assertInWithGeneratorInvariants(foo)
    285         self.assertAfterWithManagerInvariantsNoError(mock_a)
    286         self.assertAfterWithGeneratorInvariantsNoError(foo)
    287 
    288 
    289 class NestedNonexceptionalTestCase(unittest.TestCase,
    290     ContextmanagerAssertionMixin):
    291     def testSingleArgInlineGeneratorSyntax(self):
    292         with Nested(mock_contextmanager_generator()):
    293             pass
    294 
    295     def testSingleArgBoundToNonTuple(self):
    296         m = mock_contextmanager_generator()
    297         # This will bind all the arguments to nested() into a single list
    298         # assigned to foo.
    299         with Nested(m) as foo:
    300             self.assertInWithManagerInvariants(m)
    301         self.assertAfterWithManagerInvariantsNoError(m)
    302 
    303     def testSingleArgBoundToSingleElementParenthesizedList(self):
    304         m = mock_contextmanager_generator()
    305         # This will bind all the arguments to nested() into a single list
    306         # assigned to foo.
    307         with Nested(m) as (foo):
    308             self.assertInWithManagerInvariants(m)
    309         self.assertAfterWithManagerInvariantsNoError(m)
    310 
    311     def testSingleArgBoundToMultipleElementTupleError(self):
    312         def shouldThrowValueError():
    313             with Nested(mock_contextmanager_generator()) as (foo, bar):
    314                 pass
    315         self.assertRaises(ValueError, shouldThrowValueError)
    316 
    317     def testSingleArgUnbound(self):
    318         mock_contextmanager = mock_contextmanager_generator()
    319         mock_nested = MockNested(mock_contextmanager)
    320         with mock_nested:
    321             self.assertInWithManagerInvariants(mock_contextmanager)
    322             self.assertInWithManagerInvariants(mock_nested)
    323         self.assertAfterWithManagerInvariantsNoError(mock_contextmanager)
    324         self.assertAfterWithManagerInvariantsNoError(mock_nested)
    325 
    326     def testMultipleArgUnbound(self):
    327         m = mock_contextmanager_generator()
    328         n = mock_contextmanager_generator()
    329         o = mock_contextmanager_generator()
    330         mock_nested = MockNested(m, n, o)
    331         with mock_nested:
    332             self.assertInWithManagerInvariants(m)
    333             self.assertInWithManagerInvariants(n)
    334             self.assertInWithManagerInvariants(o)
    335             self.assertInWithManagerInvariants(mock_nested)
    336         self.assertAfterWithManagerInvariantsNoError(m)
    337         self.assertAfterWithManagerInvariantsNoError(n)
    338         self.assertAfterWithManagerInvariantsNoError(o)
    339         self.assertAfterWithManagerInvariantsNoError(mock_nested)
    340 
    341     def testMultipleArgBound(self):
    342         mock_nested = MockNested(mock_contextmanager_generator(),
    343             mock_contextmanager_generator(), mock_contextmanager_generator())
    344         with mock_nested as (m, n, o):
    345             self.assertInWithGeneratorInvariants(m)
    346             self.assertInWithGeneratorInvariants(n)
    347             self.assertInWithGeneratorInvariants(o)
    348             self.assertInWithManagerInvariants(mock_nested)
    349         self.assertAfterWithGeneratorInvariantsNoError(m)
    350         self.assertAfterWithGeneratorInvariantsNoError(n)
    351         self.assertAfterWithGeneratorInvariantsNoError(o)
    352         self.assertAfterWithManagerInvariantsNoError(mock_nested)
    353 
    354 
    355 class ExceptionalTestCase(ContextmanagerAssertionMixin, unittest.TestCase):
    356     def testSingleResource(self):
    357         cm = mock_contextmanager_generator()
    358         def shouldThrow():
    359             with cm as self.resource:
    360                 self.assertInWithManagerInvariants(cm)
    361                 self.assertInWithGeneratorInvariants(self.resource)
    362                 self.raiseTestException()
    363         self.assertRaises(RuntimeError, shouldThrow)
    364         self.assertAfterWithManagerInvariantsWithError(cm)
    365         self.assertAfterWithGeneratorInvariantsWithError(self.resource)
    366 
    367     def testExceptionNormalized(self):
    368         cm = mock_contextmanager_generator()
    369         def shouldThrow():
    370             with cm as self.resource:
    371                 # Note this relies on the fact that 1 // 0 produces an exception
    372                 # that is not normalized immediately.
    373                 1 // 0
    374         self.assertRaises(ZeroDivisionError, shouldThrow)
    375         self.assertAfterWithManagerInvariantsWithError(cm, ZeroDivisionError)
    376 
    377     def testNestedSingleStatements(self):
    378         mock_a = mock_contextmanager_generator()
    379         mock_b = mock_contextmanager_generator()
    380         def shouldThrow():
    381             with mock_a as self.foo:
    382                 with mock_b as self.bar:
    383                     self.assertInWithManagerInvariants(mock_a)
    384                     self.assertInWithManagerInvariants(mock_b)
    385                     self.assertInWithGeneratorInvariants(self.foo)
    386                     self.assertInWithGeneratorInvariants(self.bar)
    387                     self.raiseTestException()
    388         self.assertRaises(RuntimeError, shouldThrow)
    389         self.assertAfterWithManagerInvariantsWithError(mock_a)
    390         self.assertAfterWithManagerInvariantsWithError(mock_b)
    391         self.assertAfterWithGeneratorInvariantsWithError(self.foo)
    392         self.assertAfterWithGeneratorInvariantsWithError(self.bar)
    393 
    394     def testMultipleResourcesInSingleStatement(self):
    395         cm_a = mock_contextmanager_generator()
    396         cm_b = mock_contextmanager_generator()
    397         mock_nested = MockNested(cm_a, cm_b)
    398         def shouldThrow():
    399             with mock_nested as (self.resource_a, self.resource_b):
    400                 self.assertInWithManagerInvariants(cm_a)
    401                 self.assertInWithManagerInvariants(cm_b)
    402                 self.assertInWithManagerInvariants(mock_nested)
    403                 self.assertInWithGeneratorInvariants(self.resource_a)
    404                 self.assertInWithGeneratorInvariants(self.resource_b)
    405                 self.raiseTestException()
    406         self.assertRaises(RuntimeError, shouldThrow)
    407         self.assertAfterWithManagerInvariantsWithError(cm_a)
    408         self.assertAfterWithManagerInvariantsWithError(cm_b)
    409         self.assertAfterWithManagerInvariantsWithError(mock_nested)
    410         self.assertAfterWithGeneratorInvariantsWithError(self.resource_a)
    411         self.assertAfterWithGeneratorInvariantsWithError(self.resource_b)
    412 
    413     def testNestedExceptionBeforeInnerStatement(self):
    414         mock_a = mock_contextmanager_generator()
    415         mock_b = mock_contextmanager_generator()
    416         self.bar = None
    417         def shouldThrow():
    418             with mock_a as self.foo:
    419                 self.assertInWithManagerInvariants(mock_a)
    420                 self.assertInWithGeneratorInvariants(self.foo)
    421                 self.raiseTestException()
    422                 with mock_b as self.bar:
    423                     pass
    424         self.assertRaises(RuntimeError, shouldThrow)
    425         self.assertAfterWithManagerInvariantsWithError(mock_a)
    426         self.assertAfterWithGeneratorInvariantsWithError(self.foo)
    427 
    428         # The inner statement stuff should never have been touched
    429         self.assertEqual(self.bar, None)
    430         self.assertFalse(mock_b.enter_called)
    431         self.assertFalse(mock_b.exit_called)
    432         self.assertEqual(mock_b.exit_args, None)
    433 
    434     def testNestedExceptionAfterInnerStatement(self):
    435         mock_a = mock_contextmanager_generator()
    436         mock_b = mock_contextmanager_generator()
    437         def shouldThrow():
    438             with mock_a as self.foo:
    439                 with mock_b as self.bar:
    440                     self.assertInWithManagerInvariants(mock_a)
    441                     self.assertInWithManagerInvariants(mock_b)
    442                     self.assertInWithGeneratorInvariants(self.foo)
    443                     self.assertInWithGeneratorInvariants(self.bar)
    444                 self.raiseTestException()
    445         self.assertRaises(RuntimeError, shouldThrow)
    446         self.assertAfterWithManagerInvariantsWithError(mock_a)
    447         self.assertAfterWithManagerInvariantsNoError(mock_b)
    448         self.assertAfterWithGeneratorInvariantsWithError(self.foo)
    449         self.assertAfterWithGeneratorInvariantsNoError(self.bar)
    450 
    451     def testRaisedStopIteration1(self):
    452         # From bug 1462485
    453         @contextmanager
    454         def cm():
    455             yield
    456 
    457         def shouldThrow():
    458             with cm():
    459                 raise StopIteration("from with")
    460 
    461         with self.assertWarnsRegex(DeprecationWarning, "StopIteration"):
    462             self.assertRaises(StopIteration, shouldThrow)
    463 
    464     def testRaisedStopIteration2(self):
    465         # From bug 1462485
    466         class cm(object):
    467             def __enter__(self):
    468                 pass
    469             def __exit__(self, type, value, traceback):
    470                 pass
    471 
    472         def shouldThrow():
    473             with cm():
    474                 raise StopIteration("from with")
    475 
    476         self.assertRaises(StopIteration, shouldThrow)
    477 
    478     def testRaisedStopIteration3(self):
    479         # Another variant where the exception hasn't been instantiated
    480         # From bug 1705170
    481         @contextmanager
    482         def cm():
    483             yield
    484 
    485         def shouldThrow():
    486             with cm():
    487                 raise next(iter([]))
    488 
    489         with self.assertWarnsRegex(DeprecationWarning, "StopIteration"):
    490             self.assertRaises(StopIteration, shouldThrow)
    491 
    492     def testRaisedGeneratorExit1(self):
    493         # From bug 1462485
    494         @contextmanager
    495         def cm():
    496             yield
    497 
    498         def shouldThrow():
    499             with cm():
    500                 raise GeneratorExit("from with")
    501 
    502         self.assertRaises(GeneratorExit, shouldThrow)
    503 
    504     def testRaisedGeneratorExit2(self):
    505         # From bug 1462485
    506         class cm (object):
    507             def __enter__(self):
    508                 pass
    509             def __exit__(self, type, value, traceback):
    510                 pass
    511 
    512         def shouldThrow():
    513             with cm():
    514                 raise GeneratorExit("from with")
    515 
    516         self.assertRaises(GeneratorExit, shouldThrow)
    517 
    518     def testErrorsInBool(self):
    519         # issue4589: __exit__ return code may raise an exception
    520         # when looking at its truth value.
    521 
    522         class cm(object):
    523             def __init__(self, bool_conversion):
    524                 class Bool:
    525                     def __bool__(self):
    526                         return bool_conversion()
    527                 self.exit_result = Bool()
    528             def __enter__(self):
    529                 return 3
    530             def __exit__(self, a, b, c):
    531                 return self.exit_result
    532 
    533         def trueAsBool():
    534             with cm(lambda: True):
    535                 self.fail("Should NOT see this")
    536         trueAsBool()
    537 
    538         def falseAsBool():
    539             with cm(lambda: False):
    540                 self.fail("Should raise")
    541         self.assertRaises(AssertionError, falseAsBool)
    542 
    543         def failAsBool():
    544             with cm(lambda: 1//0):
    545                 self.fail("Should NOT see this")
    546         self.assertRaises(ZeroDivisionError, failAsBool)
    547 
    548 
    549 class NonLocalFlowControlTestCase(unittest.TestCase):
    550 
    551     def testWithBreak(self):
    552         counter = 0
    553         while True:
    554             counter += 1
    555             with mock_contextmanager_generator():
    556                 counter += 10
    557                 break
    558             counter += 100 # Not reached
    559         self.assertEqual(counter, 11)
    560 
    561     def testWithContinue(self):
    562         counter = 0
    563         while True:
    564             counter += 1
    565             if counter > 2:
    566                 break
    567             with mock_contextmanager_generator():
    568                 counter += 10
    569                 continue
    570             counter += 100 # Not reached
    571         self.assertEqual(counter, 12)
    572 
    573     def testWithReturn(self):
    574         def foo():
    575             counter = 0
    576             while True:
    577                 counter += 1
    578                 with mock_contextmanager_generator():
    579                     counter += 10
    580                     return counter
    581                 counter += 100 # Not reached
    582         self.assertEqual(foo(), 11)
    583 
    584     def testWithYield(self):
    585         def gen():
    586             with mock_contextmanager_generator():
    587                 yield 12
    588                 yield 13
    589         x = list(gen())
    590         self.assertEqual(x, [12, 13])
    591 
    592     def testWithRaise(self):
    593         counter = 0
    594         try:
    595             counter += 1
    596             with mock_contextmanager_generator():
    597                 counter += 10
    598                 raise RuntimeError
    599             counter += 100 # Not reached
    600         except RuntimeError:
    601             self.assertEqual(counter, 11)
    602         else:
    603             self.fail("Didn't raise RuntimeError")
    604 
    605 
    606 class AssignmentTargetTestCase(unittest.TestCase):
    607 
    608     def testSingleComplexTarget(self):
    609         targets = {1: [0, 1, 2]}
    610         with mock_contextmanager_generator() as targets[1][0]:
    611             self.assertEqual(list(targets.keys()), [1])
    612             self.assertEqual(targets[1][0].__class__, MockResource)
    613         with mock_contextmanager_generator() as list(targets.values())[0][1]:
    614             self.assertEqual(list(targets.keys()), [1])
    615             self.assertEqual(targets[1][1].__class__, MockResource)
    616         with mock_contextmanager_generator() as targets[2]:
    617             keys = list(targets.keys())
    618             keys.sort()
    619             self.assertEqual(keys, [1, 2])
    620         class C: pass
    621         blah = C()
    622         with mock_contextmanager_generator() as blah.foo:
    623             self.assertEqual(hasattr(blah, "foo"), True)
    624 
    625     def testMultipleComplexTargets(self):
    626         class C:
    627             def __enter__(self): return 1, 2, 3
    628             def __exit__(self, t, v, tb): pass
    629         targets = {1: [0, 1, 2]}
    630         with C() as (targets[1][0], targets[1][1], targets[1][2]):
    631             self.assertEqual(targets, {1: [1, 2, 3]})
    632         with C() as (list(targets.values())[0][2], list(targets.values())[0][1], list(targets.values())[0][0]):
    633             self.assertEqual(targets, {1: [3, 2, 1]})
    634         with C() as (targets[1], targets[2], targets[3]):
    635             self.assertEqual(targets, {1: 1, 2: 2, 3: 3})
    636         class B: pass
    637         blah = B()
    638         with C() as (blah.one, blah.two, blah.three):
    639             self.assertEqual(blah.one, 1)
    640             self.assertEqual(blah.two, 2)
    641             self.assertEqual(blah.three, 3)
    642 
    643 
    644 class ExitSwallowsExceptionTestCase(unittest.TestCase):
    645 
    646     def testExitTrueSwallowsException(self):
    647         class AfricanSwallow:
    648             def __enter__(self): pass
    649             def __exit__(self, t, v, tb): return True
    650         try:
    651             with AfricanSwallow():
    652                 1/0
    653         except ZeroDivisionError:
    654             self.fail("ZeroDivisionError should have been swallowed")
    655 
    656     def testExitFalseDoesntSwallowException(self):
    657         class EuropeanSwallow:
    658             def __enter__(self): pass
    659             def __exit__(self, t, v, tb): return False
    660         try:
    661             with EuropeanSwallow():
    662                 1/0
    663         except ZeroDivisionError:
    664             pass
    665         else:
    666             self.fail("ZeroDivisionError should have been raised")
    667 
    668 
    669 class NestedWith(unittest.TestCase):
    670 
    671     class Dummy(object):
    672         def __init__(self, value=None, gobble=False):
    673             if value is None:
    674                 value = self
    675             self.value = value
    676             self.gobble = gobble
    677             self.enter_called = False
    678             self.exit_called = False
    679 
    680         def __enter__(self):
    681             self.enter_called = True
    682             return self.value
    683 
    684         def __exit__(self, *exc_info):
    685             self.exit_called = True
    686             self.exc_info = exc_info
    687             if self.gobble:
    688                 return True
    689 
    690     class InitRaises(object):
    691         def __init__(self): raise RuntimeError()
    692 
    693     class EnterRaises(object):
    694         def __enter__(self): raise RuntimeError()
    695         def __exit__(self, *exc_info): pass
    696 
    697     class ExitRaises(object):
    698         def __enter__(self): pass
    699         def __exit__(self, *exc_info): raise RuntimeError()
    700 
    701     def testNoExceptions(self):
    702         with self.Dummy() as a, self.Dummy() as b:
    703             self.assertTrue(a.enter_called)
    704             self.assertTrue(b.enter_called)
    705         self.assertTrue(a.exit_called)
    706         self.assertTrue(b.exit_called)
    707 
    708     def testExceptionInExprList(self):
    709         try:
    710             with self.Dummy() as a, self.InitRaises():
    711                 pass
    712         except:
    713             pass
    714         self.assertTrue(a.enter_called)
    715         self.assertTrue(a.exit_called)
    716 
    717     def testExceptionInEnter(self):
    718         try:
    719             with self.Dummy() as a, self.EnterRaises():
    720                 self.fail('body of bad with executed')
    721         except RuntimeError:
    722             pass
    723         else:
    724             self.fail('RuntimeError not reraised')
    725         self.assertTrue(a.enter_called)
    726         self.assertTrue(a.exit_called)
    727 
    728     def testExceptionInExit(self):
    729         body_executed = False
    730         with self.Dummy(gobble=True) as a, self.ExitRaises():
    731             body_executed = True
    732         self.assertTrue(a.enter_called)
    733         self.assertTrue(a.exit_called)
    734         self.assertTrue(body_executed)
    735         self.assertNotEqual(a.exc_info[0], None)
    736 
    737     def testEnterReturnsTuple(self):
    738         with self.Dummy(value=(1,2)) as (a1, a2), \
    739              self.Dummy(value=(10, 20)) as (b1, b2):
    740             self.assertEqual(1, a1)
    741             self.assertEqual(2, a2)
    742             self.assertEqual(10, b1)
    743             self.assertEqual(20, b2)
    744 
    745 if __name__ == '__main__':
    746     unittest.main()
    747