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.assertRaisesRegex(StopIteration, 'from with'):
    462             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         with self.assertRaisesRegex(StopIteration, 'from with'):
    477             shouldThrow()
    478 
    479     def testRaisedStopIteration3(self):
    480         # Another variant where the exception hasn't been instantiated
    481         # From bug 1705170
    482         @contextmanager
    483         def cm():
    484             yield
    485 
    486         def shouldThrow():
    487             with cm():
    488                 raise next(iter([]))
    489 
    490         with self.assertRaises(StopIteration):
    491             shouldThrow()
    492 
    493     def testRaisedGeneratorExit1(self):
    494         # From bug 1462485
    495         @contextmanager
    496         def cm():
    497             yield
    498 
    499         def shouldThrow():
    500             with cm():
    501                 raise GeneratorExit("from with")
    502 
    503         self.assertRaises(GeneratorExit, shouldThrow)
    504 
    505     def testRaisedGeneratorExit2(self):
    506         # From bug 1462485
    507         class cm (object):
    508             def __enter__(self):
    509                 pass
    510             def __exit__(self, type, value, traceback):
    511                 pass
    512 
    513         def shouldThrow():
    514             with cm():
    515                 raise GeneratorExit("from with")
    516 
    517         self.assertRaises(GeneratorExit, shouldThrow)
    518 
    519     def testErrorsInBool(self):
    520         # issue4589: __exit__ return code may raise an exception
    521         # when looking at its truth value.
    522 
    523         class cm(object):
    524             def __init__(self, bool_conversion):
    525                 class Bool:
    526                     def __bool__(self):
    527                         return bool_conversion()
    528                 self.exit_result = Bool()
    529             def __enter__(self):
    530                 return 3
    531             def __exit__(self, a, b, c):
    532                 return self.exit_result
    533 
    534         def trueAsBool():
    535             with cm(lambda: True):
    536                 self.fail("Should NOT see this")
    537         trueAsBool()
    538 
    539         def falseAsBool():
    540             with cm(lambda: False):
    541                 self.fail("Should raise")
    542         self.assertRaises(AssertionError, falseAsBool)
    543 
    544         def failAsBool():
    545             with cm(lambda: 1//0):
    546                 self.fail("Should NOT see this")
    547         self.assertRaises(ZeroDivisionError, failAsBool)
    548 
    549 
    550 class NonLocalFlowControlTestCase(unittest.TestCase):
    551 
    552     def testWithBreak(self):
    553         counter = 0
    554         while True:
    555             counter += 1
    556             with mock_contextmanager_generator():
    557                 counter += 10
    558                 break
    559             counter += 100 # Not reached
    560         self.assertEqual(counter, 11)
    561 
    562     def testWithContinue(self):
    563         counter = 0
    564         while True:
    565             counter += 1
    566             if counter > 2:
    567                 break
    568             with mock_contextmanager_generator():
    569                 counter += 10
    570                 continue
    571             counter += 100 # Not reached
    572         self.assertEqual(counter, 12)
    573 
    574     def testWithReturn(self):
    575         def foo():
    576             counter = 0
    577             while True:
    578                 counter += 1
    579                 with mock_contextmanager_generator():
    580                     counter += 10
    581                     return counter
    582                 counter += 100 # Not reached
    583         self.assertEqual(foo(), 11)
    584 
    585     def testWithYield(self):
    586         def gen():
    587             with mock_contextmanager_generator():
    588                 yield 12
    589                 yield 13
    590         x = list(gen())
    591         self.assertEqual(x, [12, 13])
    592 
    593     def testWithRaise(self):
    594         counter = 0
    595         try:
    596             counter += 1
    597             with mock_contextmanager_generator():
    598                 counter += 10
    599                 raise RuntimeError
    600             counter += 100 # Not reached
    601         except RuntimeError:
    602             self.assertEqual(counter, 11)
    603         else:
    604             self.fail("Didn't raise RuntimeError")
    605 
    606 
    607 class AssignmentTargetTestCase(unittest.TestCase):
    608 
    609     def testSingleComplexTarget(self):
    610         targets = {1: [0, 1, 2]}
    611         with mock_contextmanager_generator() as targets[1][0]:
    612             self.assertEqual(list(targets.keys()), [1])
    613             self.assertEqual(targets[1][0].__class__, MockResource)
    614         with mock_contextmanager_generator() as list(targets.values())[0][1]:
    615             self.assertEqual(list(targets.keys()), [1])
    616             self.assertEqual(targets[1][1].__class__, MockResource)
    617         with mock_contextmanager_generator() as targets[2]:
    618             keys = list(targets.keys())
    619             keys.sort()
    620             self.assertEqual(keys, [1, 2])
    621         class C: pass
    622         blah = C()
    623         with mock_contextmanager_generator() as blah.foo:
    624             self.assertEqual(hasattr(blah, "foo"), True)
    625 
    626     def testMultipleComplexTargets(self):
    627         class C:
    628             def __enter__(self): return 1, 2, 3
    629             def __exit__(self, t, v, tb): pass
    630         targets = {1: [0, 1, 2]}
    631         with C() as (targets[1][0], targets[1][1], targets[1][2]):
    632             self.assertEqual(targets, {1: [1, 2, 3]})
    633         with C() as (list(targets.values())[0][2], list(targets.values())[0][1], list(targets.values())[0][0]):
    634             self.assertEqual(targets, {1: [3, 2, 1]})
    635         with C() as (targets[1], targets[2], targets[3]):
    636             self.assertEqual(targets, {1: 1, 2: 2, 3: 3})
    637         class B: pass
    638         blah = B()
    639         with C() as (blah.one, blah.two, blah.three):
    640             self.assertEqual(blah.one, 1)
    641             self.assertEqual(blah.two, 2)
    642             self.assertEqual(blah.three, 3)
    643 
    644 
    645 class ExitSwallowsExceptionTestCase(unittest.TestCase):
    646 
    647     def testExitTrueSwallowsException(self):
    648         class AfricanSwallow:
    649             def __enter__(self): pass
    650             def __exit__(self, t, v, tb): return True
    651         try:
    652             with AfricanSwallow():
    653                 1/0
    654         except ZeroDivisionError:
    655             self.fail("ZeroDivisionError should have been swallowed")
    656 
    657     def testExitFalseDoesntSwallowException(self):
    658         class EuropeanSwallow:
    659             def __enter__(self): pass
    660             def __exit__(self, t, v, tb): return False
    661         try:
    662             with EuropeanSwallow():
    663                 1/0
    664         except ZeroDivisionError:
    665             pass
    666         else:
    667             self.fail("ZeroDivisionError should have been raised")
    668 
    669 
    670 class NestedWith(unittest.TestCase):
    671 
    672     class Dummy(object):
    673         def __init__(self, value=None, gobble=False):
    674             if value is None:
    675                 value = self
    676             self.value = value
    677             self.gobble = gobble
    678             self.enter_called = False
    679             self.exit_called = False
    680 
    681         def __enter__(self):
    682             self.enter_called = True
    683             return self.value
    684 
    685         def __exit__(self, *exc_info):
    686             self.exit_called = True
    687             self.exc_info = exc_info
    688             if self.gobble:
    689                 return True
    690 
    691     class InitRaises(object):
    692         def __init__(self): raise RuntimeError()
    693 
    694     class EnterRaises(object):
    695         def __enter__(self): raise RuntimeError()
    696         def __exit__(self, *exc_info): pass
    697 
    698     class ExitRaises(object):
    699         def __enter__(self): pass
    700         def __exit__(self, *exc_info): raise RuntimeError()
    701 
    702     def testNoExceptions(self):
    703         with self.Dummy() as a, self.Dummy() as b:
    704             self.assertTrue(a.enter_called)
    705             self.assertTrue(b.enter_called)
    706         self.assertTrue(a.exit_called)
    707         self.assertTrue(b.exit_called)
    708 
    709     def testExceptionInExprList(self):
    710         try:
    711             with self.Dummy() as a, self.InitRaises():
    712                 pass
    713         except:
    714             pass
    715         self.assertTrue(a.enter_called)
    716         self.assertTrue(a.exit_called)
    717 
    718     def testExceptionInEnter(self):
    719         try:
    720             with self.Dummy() as a, self.EnterRaises():
    721                 self.fail('body of bad with executed')
    722         except RuntimeError:
    723             pass
    724         else:
    725             self.fail('RuntimeError not reraised')
    726         self.assertTrue(a.enter_called)
    727         self.assertTrue(a.exit_called)
    728 
    729     def testExceptionInExit(self):
    730         body_executed = False
    731         with self.Dummy(gobble=True) as a, self.ExitRaises():
    732             body_executed = True
    733         self.assertTrue(a.enter_called)
    734         self.assertTrue(a.exit_called)
    735         self.assertTrue(body_executed)
    736         self.assertNotEqual(a.exc_info[0], None)
    737 
    738     def testEnterReturnsTuple(self):
    739         with self.Dummy(value=(1,2)) as (a1, a2), \
    740              self.Dummy(value=(10, 20)) as (b1, b2):
    741             self.assertEqual(1, a1)
    742             self.assertEqual(2, a2)
    743             self.assertEqual(10, b1)
    744             self.assertEqual(20, b2)
    745 
    746 if __name__ == '__main__':
    747     unittest.main()
    748