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