• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7import sys
8import unittest
9from collections import deque
10from contextlib import _GeneratorContextManager, contextmanager
11
12
13class 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
31def mock_contextmanager(func):
32    def helper(*args, **kwds):
33        return MockContextManager(func, args, kwds)
34    return helper
35
36
37class MockResource(object):
38    def __init__(self):
39        self.yielded = False
40        self.stopped = False
41
42
43@mock_contextmanager
44def mock_contextmanager_generator():
45    mock = MockResource()
46    try:
47        mock.yielded = True
48        yield mock
49    finally:
50        mock.stopped = True
51
52
53class 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
89class 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
106class 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
189class 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
236class 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
289class 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
355class 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
550class 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
607class 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
645class 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
670class 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
746if __name__ == '__main__':
747    unittest.main()
748