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