blob: ce21c3df814037fb9da0f08250a1833722b3a02e [file] [log] [blame]
Benjamin Peterson9a80fa82012-01-13 14:39:38 -05001# -*- coding: utf-8 -*-
2
3"""
4Test suite for PEP 380 implementation
5
6adapted from original tests written by Greg Ewing
7see <http://www.cosc.canterbury.ac.nz/greg.ewing/python/yield-from/YieldFrom-Python3.1.2-rev5.zip>
8"""
9
10import unittest
Benjamin Peterson2afe6ae2012-03-15 15:37:39 -050011import inspect
Benjamin Peterson9a80fa82012-01-13 14:39:38 -050012
Benjamin Petersond979c722013-07-27 14:06:56 -070013from test.support import captured_stderr, disable_gc, gc_collect
Benjamin Peterson9a80fa82012-01-13 14:39:38 -050014
15class TestPEP380Operation(unittest.TestCase):
16 """
17 Test semantics.
18 """
19
20 def test_delegation_of_initial_next_to_subgenerator(self):
21 """
22 Test delegation of initial next() call to subgenerator
23 """
24 trace = []
25 def g1():
26 trace.append("Starting g1")
27 yield from g2()
28 trace.append("Finishing g1")
29 def g2():
30 trace.append("Starting g2")
31 yield 42
32 trace.append("Finishing g2")
33 for x in g1():
34 trace.append("Yielded %s" % (x,))
35 self.assertEqual(trace,[
36 "Starting g1",
37 "Starting g2",
38 "Yielded 42",
39 "Finishing g2",
40 "Finishing g1",
41 ])
42
43 def test_raising_exception_in_initial_next_call(self):
44 """
45 Test raising exception in initial next() call
46 """
47 trace = []
48 def g1():
49 try:
50 trace.append("Starting g1")
51 yield from g2()
52 finally:
53 trace.append("Finishing g1")
54 def g2():
55 try:
56 trace.append("Starting g2")
57 raise ValueError("spanish inquisition occurred")
58 finally:
59 trace.append("Finishing g2")
60 try:
61 for x in g1():
62 trace.append("Yielded %s" % (x,))
63 except ValueError as e:
64 self.assertEqual(e.args[0], "spanish inquisition occurred")
65 else:
66 self.fail("subgenerator failed to raise ValueError")
67 self.assertEqual(trace,[
68 "Starting g1",
69 "Starting g2",
70 "Finishing g2",
71 "Finishing g1",
72 ])
73
74 def test_delegation_of_next_call_to_subgenerator(self):
75 """
76 Test delegation of next() call to subgenerator
77 """
78 trace = []
79 def g1():
80 trace.append("Starting g1")
81 yield "g1 ham"
82 yield from g2()
83 yield "g1 eggs"
84 trace.append("Finishing g1")
85 def g2():
86 trace.append("Starting g2")
87 yield "g2 spam"
88 yield "g2 more spam"
89 trace.append("Finishing g2")
90 for x in g1():
91 trace.append("Yielded %s" % (x,))
92 self.assertEqual(trace,[
93 "Starting g1",
94 "Yielded g1 ham",
95 "Starting g2",
96 "Yielded g2 spam",
97 "Yielded g2 more spam",
98 "Finishing g2",
99 "Yielded g1 eggs",
100 "Finishing g1",
101 ])
102
103 def test_raising_exception_in_delegated_next_call(self):
104 """
105 Test raising exception in delegated next() call
106 """
107 trace = []
108 def g1():
109 try:
110 trace.append("Starting g1")
111 yield "g1 ham"
112 yield from g2()
113 yield "g1 eggs"
114 finally:
115 trace.append("Finishing g1")
116 def g2():
117 try:
118 trace.append("Starting g2")
119 yield "g2 spam"
120 raise ValueError("hovercraft is full of eels")
121 yield "g2 more spam"
122 finally:
123 trace.append("Finishing g2")
124 try:
125 for x in g1():
126 trace.append("Yielded %s" % (x,))
127 except ValueError as e:
128 self.assertEqual(e.args[0], "hovercraft is full of eels")
129 else:
130 self.fail("subgenerator failed to raise ValueError")
131 self.assertEqual(trace,[
132 "Starting g1",
133 "Yielded g1 ham",
134 "Starting g2",
135 "Yielded g2 spam",
136 "Finishing g2",
137 "Finishing g1",
138 ])
139
140 def test_delegation_of_send(self):
141 """
142 Test delegation of send()
143 """
144 trace = []
145 def g1():
146 trace.append("Starting g1")
147 x = yield "g1 ham"
148 trace.append("g1 received %s" % (x,))
149 yield from g2()
150 x = yield "g1 eggs"
151 trace.append("g1 received %s" % (x,))
152 trace.append("Finishing g1")
153 def g2():
154 trace.append("Starting g2")
155 x = yield "g2 spam"
156 trace.append("g2 received %s" % (x,))
157 x = yield "g2 more spam"
158 trace.append("g2 received %s" % (x,))
159 trace.append("Finishing g2")
160 g = g1()
161 y = next(g)
162 x = 1
163 try:
164 while 1:
165 y = g.send(x)
166 trace.append("Yielded %s" % (y,))
167 x += 1
168 except StopIteration:
169 pass
170 self.assertEqual(trace,[
171 "Starting g1",
172 "g1 received 1",
173 "Starting g2",
174 "Yielded g2 spam",
175 "g2 received 2",
176 "Yielded g2 more spam",
177 "g2 received 3",
178 "Finishing g2",
179 "Yielded g1 eggs",
180 "g1 received 4",
181 "Finishing g1",
182 ])
183
184 def test_handling_exception_while_delegating_send(self):
185 """
186 Test handling exception while delegating 'send'
187 """
188 trace = []
189 def g1():
190 trace.append("Starting g1")
191 x = yield "g1 ham"
192 trace.append("g1 received %s" % (x,))
193 yield from g2()
194 x = yield "g1 eggs"
195 trace.append("g1 received %s" % (x,))
196 trace.append("Finishing g1")
197 def g2():
198 trace.append("Starting g2")
199 x = yield "g2 spam"
200 trace.append("g2 received %s" % (x,))
201 raise ValueError("hovercraft is full of eels")
202 x = yield "g2 more spam"
203 trace.append("g2 received %s" % (x,))
204 trace.append("Finishing g2")
205 def run():
206 g = g1()
207 y = next(g)
208 x = 1
209 try:
210 while 1:
211 y = g.send(x)
212 trace.append("Yielded %s" % (y,))
213 x += 1
214 except StopIteration:
215 trace.append("StopIteration")
216 self.assertRaises(ValueError,run)
217 self.assertEqual(trace,[
218 "Starting g1",
219 "g1 received 1",
220 "Starting g2",
221 "Yielded g2 spam",
222 "g2 received 2",
223 ])
224
225 def test_delegating_close(self):
226 """
227 Test delegating 'close'
228 """
229 trace = []
230 def g1():
231 try:
232 trace.append("Starting g1")
233 yield "g1 ham"
234 yield from g2()
235 yield "g1 eggs"
236 finally:
237 trace.append("Finishing g1")
238 def g2():
239 try:
240 trace.append("Starting g2")
241 yield "g2 spam"
242 yield "g2 more spam"
243 finally:
244 trace.append("Finishing g2")
245 g = g1()
246 for i in range(2):
247 x = next(g)
248 trace.append("Yielded %s" % (x,))
249 g.close()
250 self.assertEqual(trace,[
251 "Starting g1",
252 "Yielded g1 ham",
253 "Starting g2",
254 "Yielded g2 spam",
255 "Finishing g2",
256 "Finishing g1"
257 ])
258
259 def test_handing_exception_while_delegating_close(self):
260 """
261 Test handling exception while delegating 'close'
262 """
263 trace = []
264 def g1():
265 try:
266 trace.append("Starting g1")
267 yield "g1 ham"
268 yield from g2()
269 yield "g1 eggs"
270 finally:
271 trace.append("Finishing g1")
272 def g2():
273 try:
274 trace.append("Starting g2")
275 yield "g2 spam"
276 yield "g2 more spam"
277 finally:
278 trace.append("Finishing g2")
279 raise ValueError("nybbles have exploded with delight")
280 try:
281 g = g1()
282 for i in range(2):
283 x = next(g)
284 trace.append("Yielded %s" % (x,))
285 g.close()
286 except ValueError as e:
287 self.assertEqual(e.args[0], "nybbles have exploded with delight")
288 self.assertIsInstance(e.__context__, GeneratorExit)
289 else:
290 self.fail("subgenerator failed to raise ValueError")
291 self.assertEqual(trace,[
292 "Starting g1",
293 "Yielded g1 ham",
294 "Starting g2",
295 "Yielded g2 spam",
296 "Finishing g2",
297 "Finishing g1",
298 ])
299
300 def test_delegating_throw(self):
301 """
302 Test delegating 'throw'
303 """
304 trace = []
305 def g1():
306 try:
307 trace.append("Starting g1")
308 yield "g1 ham"
309 yield from g2()
310 yield "g1 eggs"
311 finally:
312 trace.append("Finishing g1")
313 def g2():
314 try:
315 trace.append("Starting g2")
316 yield "g2 spam"
317 yield "g2 more spam"
318 finally:
319 trace.append("Finishing g2")
320 try:
321 g = g1()
322 for i in range(2):
323 x = next(g)
324 trace.append("Yielded %s" % (x,))
325 e = ValueError("tomato ejected")
326 g.throw(e)
327 except ValueError as e:
328 self.assertEqual(e.args[0], "tomato ejected")
329 else:
330 self.fail("subgenerator failed to raise ValueError")
331 self.assertEqual(trace,[
332 "Starting g1",
333 "Yielded g1 ham",
334 "Starting g2",
335 "Yielded g2 spam",
336 "Finishing g2",
337 "Finishing g1",
338 ])
339
340 def test_value_attribute_of_StopIteration_exception(self):
341 """
342 Test 'value' attribute of StopIteration exception
343 """
344 trace = []
345 def pex(e):
346 trace.append("%s: %s" % (e.__class__.__name__, e))
347 trace.append("value = %s" % (e.value,))
348 e = StopIteration()
349 pex(e)
350 e = StopIteration("spam")
351 pex(e)
352 e.value = "eggs"
353 pex(e)
354 self.assertEqual(trace,[
355 "StopIteration: ",
356 "value = None",
357 "StopIteration: spam",
358 "value = spam",
359 "StopIteration: spam",
360 "value = eggs",
361 ])
362
363
Amaury Forgeot d'Arce557da82012-01-13 21:06:12 +0100364 def test_exception_value_crash(self):
365 # There used to be a refcount error when the return value
366 # stored in the StopIteration has a refcount of 1.
367 def g1():
368 yield from g2()
369 def g2():
370 yield "g2"
371 return [42]
372 self.assertEqual(list(g1()), ["g2"])
373
374
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500375 def test_generator_return_value(self):
376 """
377 Test generator return value
378 """
379 trace = []
380 def g1():
381 trace.append("Starting g1")
382 yield "g1 ham"
383 ret = yield from g2()
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200384 trace.append("g2 returned %r" % (ret,))
385 for v in 1, (2,), StopIteration(3):
386 ret = yield from g2(v)
387 trace.append("g2 returned %r" % (ret,))
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500388 yield "g1 eggs"
389 trace.append("Finishing g1")
390 def g2(v = None):
391 trace.append("Starting g2")
392 yield "g2 spam"
393 yield "g2 more spam"
394 trace.append("Finishing g2")
395 if v:
396 return v
397 for x in g1():
398 trace.append("Yielded %s" % (x,))
399 self.assertEqual(trace,[
400 "Starting g1",
401 "Yielded g1 ham",
402 "Starting g2",
403 "Yielded g2 spam",
404 "Yielded g2 more spam",
405 "Finishing g2",
406 "g2 returned None",
407 "Starting g2",
408 "Yielded g2 spam",
409 "Yielded g2 more spam",
410 "Finishing g2",
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200411 "g2 returned 1",
412 "Starting g2",
413 "Yielded g2 spam",
414 "Yielded g2 more spam",
415 "Finishing g2",
416 "g2 returned (2,)",
417 "Starting g2",
418 "Yielded g2 spam",
419 "Yielded g2 more spam",
420 "Finishing g2",
Serhiy Storchakaf8a4c032017-11-15 17:53:28 +0200421 "g2 returned StopIteration(3)",
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500422 "Yielded g1 eggs",
423 "Finishing g1",
424 ])
425
426 def test_delegation_of_next_to_non_generator(self):
427 """
428 Test delegation of next() to non-generator
429 """
430 trace = []
431 def g():
432 yield from range(3)
433 for x in g():
434 trace.append("Yielded %s" % (x,))
435 self.assertEqual(trace,[
436 "Yielded 0",
437 "Yielded 1",
438 "Yielded 2",
439 ])
440
441
442 def test_conversion_of_sendNone_to_next(self):
443 """
444 Test conversion of send(None) to next()
445 """
446 trace = []
447 def g():
448 yield from range(3)
449 gi = g()
450 for x in range(3):
451 y = gi.send(None)
452 trace.append("Yielded: %s" % (y,))
453 self.assertEqual(trace,[
454 "Yielded: 0",
455 "Yielded: 1",
456 "Yielded: 2",
457 ])
458
459 def test_delegation_of_close_to_non_generator(self):
460 """
461 Test delegation of close() to non-generator
462 """
463 trace = []
464 def g():
465 try:
466 trace.append("starting g")
467 yield from range(3)
468 trace.append("g should not be here")
469 finally:
470 trace.append("finishing g")
471 gi = g()
472 next(gi)
473 with captured_stderr() as output:
474 gi.close()
475 self.assertEqual(output.getvalue(), '')
476 self.assertEqual(trace,[
477 "starting g",
478 "finishing g",
479 ])
480
481 def test_delegating_throw_to_non_generator(self):
482 """
483 Test delegating 'throw' to non-generator
484 """
485 trace = []
486 def g():
487 try:
488 trace.append("Starting g")
489 yield from range(10)
490 finally:
491 trace.append("Finishing g")
492 try:
493 gi = g()
494 for i in range(5):
495 x = next(gi)
496 trace.append("Yielded %s" % (x,))
497 e = ValueError("tomato ejected")
498 gi.throw(e)
499 except ValueError as e:
500 self.assertEqual(e.args[0],"tomato ejected")
501 else:
502 self.fail("subgenerator failed to raise ValueError")
503 self.assertEqual(trace,[
504 "Starting g",
505 "Yielded 0",
506 "Yielded 1",
507 "Yielded 2",
508 "Yielded 3",
509 "Yielded 4",
510 "Finishing g",
511 ])
512
513 def test_attempting_to_send_to_non_generator(self):
514 """
515 Test attempting to send to non-generator
516 """
517 trace = []
518 def g():
519 try:
520 trace.append("starting g")
521 yield from range(3)
522 trace.append("g should not be here")
523 finally:
524 trace.append("finishing g")
525 try:
526 gi = g()
527 next(gi)
528 for x in range(3):
529 y = gi.send(42)
Serhiy Storchaka5da57022012-12-31 11:31:41 +0200530 trace.append("Should not have yielded: %s" % (y,))
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500531 except AttributeError as e:
532 self.assertIn("send", e.args[0])
533 else:
534 self.fail("was able to send into non-generator")
535 self.assertEqual(trace,[
536 "starting g",
537 "finishing g",
538 ])
539
540 def test_broken_getattr_handling(self):
541 """
542 Test subiterator with a broken getattr implementation
543 """
544 class Broken:
545 def __iter__(self):
546 return self
547 def __next__(self):
548 return 1
549 def __getattr__(self, attr):
550 1/0
551
552 def g():
553 yield from Broken()
554
555 with self.assertRaises(ZeroDivisionError):
556 gi = g()
557 self.assertEqual(next(gi), 1)
558 gi.send(1)
559
560 with self.assertRaises(ZeroDivisionError):
561 gi = g()
562 self.assertEqual(next(gi), 1)
563 gi.throw(AttributeError)
564
565 with captured_stderr() as output:
566 gi = g()
567 self.assertEqual(next(gi), 1)
568 gi.close()
569 self.assertIn('ZeroDivisionError', output.getvalue())
570
571 def test_exception_in_initial_next_call(self):
572 """
573 Test exception in initial next() call
574 """
575 trace = []
576 def g1():
577 trace.append("g1 about to yield from g2")
578 yield from g2()
579 trace.append("g1 should not be here")
580 def g2():
581 yield 1/0
582 def run():
583 gi = g1()
584 next(gi)
585 self.assertRaises(ZeroDivisionError,run)
586 self.assertEqual(trace,[
587 "g1 about to yield from g2"
588 ])
589
590 def test_attempted_yield_from_loop(self):
591 """
592 Test attempted yield-from loop
593 """
594 trace = []
595 def g1():
596 trace.append("g1: starting")
597 yield "y1"
598 trace.append("g1: about to yield from g2")
599 yield from g2()
600 trace.append("g1 should not be here")
601
602 def g2():
603 trace.append("g2: starting")
604 yield "y2"
605 trace.append("g2: about to yield from g1")
606 yield from gi
607 trace.append("g2 should not be here")
608 try:
609 gi = g1()
610 for y in gi:
611 trace.append("Yielded: %s" % (y,))
612 except ValueError as e:
613 self.assertEqual(e.args[0],"generator already executing")
614 else:
615 self.fail("subgenerator didn't raise ValueError")
616 self.assertEqual(trace,[
617 "g1: starting",
618 "Yielded: y1",
619 "g1: about to yield from g2",
620 "g2: starting",
621 "Yielded: y2",
622 "g2: about to yield from g1",
623 ])
624
625 def test_returning_value_from_delegated_throw(self):
626 """
627 Test returning value from delegated 'throw'
628 """
629 trace = []
630 def g1():
631 try:
632 trace.append("Starting g1")
633 yield "g1 ham"
634 yield from g2()
635 yield "g1 eggs"
636 finally:
637 trace.append("Finishing g1")
638 def g2():
639 try:
640 trace.append("Starting g2")
641 yield "g2 spam"
642 yield "g2 more spam"
643 except LunchError:
644 trace.append("Caught LunchError in g2")
645 yield "g2 lunch saved"
646 yield "g2 yet more spam"
647 class LunchError(Exception):
648 pass
649 g = g1()
650 for i in range(2):
651 x = next(g)
652 trace.append("Yielded %s" % (x,))
653 e = LunchError("tomato ejected")
654 g.throw(e)
655 for x in g:
656 trace.append("Yielded %s" % (x,))
657 self.assertEqual(trace,[
658 "Starting g1",
659 "Yielded g1 ham",
660 "Starting g2",
661 "Yielded g2 spam",
662 "Caught LunchError in g2",
663 "Yielded g2 yet more spam",
664 "Yielded g1 eggs",
665 "Finishing g1",
666 ])
667
668 def test_next_and_return_with_value(self):
669 """
670 Test next and return with value
671 """
672 trace = []
673 def f(r):
674 gi = g(r)
675 next(gi)
676 try:
677 trace.append("f resuming g")
678 next(gi)
679 trace.append("f SHOULD NOT BE HERE")
680 except StopIteration as e:
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200681 trace.append("f caught %r" % (e,))
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500682 def g(r):
683 trace.append("g starting")
684 yield
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200685 trace.append("g returning %r" % (r,))
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500686 return r
687 f(None)
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200688 f(1)
689 f((2,))
690 f(StopIteration(3))
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500691 self.assertEqual(trace,[
692 "g starting",
693 "f resuming g",
694 "g returning None",
695 "f caught StopIteration()",
696 "g starting",
697 "f resuming g",
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200698 "g returning 1",
Serhiy Storchakaf8a4c032017-11-15 17:53:28 +0200699 "f caught StopIteration(1)",
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200700 "g starting",
701 "f resuming g",
702 "g returning (2,)",
Serhiy Storchakaf8a4c032017-11-15 17:53:28 +0200703 "f caught StopIteration((2,))",
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200704 "g starting",
705 "f resuming g",
Serhiy Storchakaf8a4c032017-11-15 17:53:28 +0200706 "g returning StopIteration(3)",
707 "f caught StopIteration(StopIteration(3))",
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500708 ])
709
710 def test_send_and_return_with_value(self):
711 """
712 Test send and return with value
713 """
714 trace = []
715 def f(r):
716 gi = g(r)
717 next(gi)
718 try:
719 trace.append("f sending spam to g")
720 gi.send("spam")
721 trace.append("f SHOULD NOT BE HERE")
722 except StopIteration as e:
723 trace.append("f caught %r" % (e,))
724 def g(r):
725 trace.append("g starting")
726 x = yield
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200727 trace.append("g received %r" % (x,))
728 trace.append("g returning %r" % (r,))
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500729 return r
730 f(None)
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200731 f(1)
732 f((2,))
733 f(StopIteration(3))
734 self.assertEqual(trace, [
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500735 "g starting",
736 "f sending spam to g",
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200737 "g received 'spam'",
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500738 "g returning None",
739 "f caught StopIteration()",
740 "g starting",
741 "f sending spam to g",
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200742 "g received 'spam'",
743 "g returning 1",
Serhiy Storchakaf8a4c032017-11-15 17:53:28 +0200744 'f caught StopIteration(1)',
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200745 'g starting',
746 'f sending spam to g',
747 "g received 'spam'",
748 'g returning (2,)',
Serhiy Storchakaf8a4c032017-11-15 17:53:28 +0200749 'f caught StopIteration((2,))',
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200750 'g starting',
751 'f sending spam to g',
752 "g received 'spam'",
Serhiy Storchakaf8a4c032017-11-15 17:53:28 +0200753 'g returning StopIteration(3)',
754 'f caught StopIteration(StopIteration(3))'
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500755 ])
756
757 def test_catching_exception_from_subgen_and_returning(self):
758 """
759 Test catching an exception thrown into a
760 subgenerator and returning a value
761 """
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500762 def inner():
763 try:
764 yield 1
765 except ValueError:
766 trace.append("inner caught ValueError")
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200767 return value
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500768
769 def outer():
770 v = yield from inner()
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200771 trace.append("inner returned %r to outer" % (v,))
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500772 yield v
Serhiy Storchaka60e49aa2016-11-06 18:47:03 +0200773
774 for value in 2, (2,), StopIteration(2):
775 trace = []
776 g = outer()
777 trace.append(next(g))
778 trace.append(repr(g.throw(ValueError)))
779 self.assertEqual(trace, [
780 1,
781 "inner caught ValueError",
782 "inner returned %r to outer" % (value,),
783 repr(value),
784 ])
Benjamin Peterson9a80fa82012-01-13 14:39:38 -0500785
786 def test_throwing_GeneratorExit_into_subgen_that_returns(self):
787 """
788 Test throwing GeneratorExit into a subgenerator that
789 catches it and returns normally.
790 """
791 trace = []
792 def f():
793 try:
794 trace.append("Enter f")
795 yield
796 trace.append("Exit f")
797 except GeneratorExit:
798 return
799 def g():
800 trace.append("Enter g")
801 yield from f()
802 trace.append("Exit g")
803 try:
804 gi = g()
805 next(gi)
806 gi.throw(GeneratorExit)
807 except GeneratorExit:
808 pass
809 else:
810 self.fail("subgenerator failed to raise GeneratorExit")
811 self.assertEqual(trace,[
812 "Enter g",
813 "Enter f",
814 ])
815
816 def test_throwing_GeneratorExit_into_subgenerator_that_yields(self):
817 """
818 Test throwing GeneratorExit into a subgenerator that
819 catches it and yields.
820 """
821 trace = []
822 def f():
823 try:
824 trace.append("Enter f")
825 yield
826 trace.append("Exit f")
827 except GeneratorExit:
828 yield
829 def g():
830 trace.append("Enter g")
831 yield from f()
832 trace.append("Exit g")
833 try:
834 gi = g()
835 next(gi)
836 gi.throw(GeneratorExit)
837 except RuntimeError as e:
838 self.assertEqual(e.args[0], "generator ignored GeneratorExit")
839 else:
840 self.fail("subgenerator failed to raise GeneratorExit")
841 self.assertEqual(trace,[
842 "Enter g",
843 "Enter f",
844 ])
845
846 def test_throwing_GeneratorExit_into_subgen_that_raises(self):
847 """
848 Test throwing GeneratorExit into a subgenerator that
849 catches it and raises a different exception.
850 """
851 trace = []
852 def f():
853 try:
854 trace.append("Enter f")
855 yield
856 trace.append("Exit f")
857 except GeneratorExit:
858 raise ValueError("Vorpal bunny encountered")
859 def g():
860 trace.append("Enter g")
861 yield from f()
862 trace.append("Exit g")
863 try:
864 gi = g()
865 next(gi)
866 gi.throw(GeneratorExit)
867 except ValueError as e:
868 self.assertEqual(e.args[0], "Vorpal bunny encountered")
869 self.assertIsInstance(e.__context__, GeneratorExit)
870 else:
871 self.fail("subgenerator failed to raise ValueError")
872 self.assertEqual(trace,[
873 "Enter g",
874 "Enter f",
875 ])
876
Benjamin Peterson0296a562012-01-13 14:54:31 -0500877 def test_yield_from_empty(self):
878 def g():
879 yield from ()
880 self.assertRaises(StopIteration, next, g())
881
Benjamin Peterson099a78f2012-03-07 17:57:04 -0600882 def test_delegating_generators_claim_to_be_running(self):
883 # Check with basic iteration
884 def one():
885 yield 0
886 yield from two()
887 yield 3
888 def two():
889 yield 1
890 try:
891 yield from g1
892 except ValueError:
893 pass
894 yield 2
895 g1 = one()
896 self.assertEqual(list(g1), [0, 1, 2, 3])
897 # Check with send
898 g1 = one()
899 res = [next(g1)]
900 try:
901 while True:
902 res.append(g1.send(42))
903 except StopIteration:
904 pass
905 self.assertEqual(res, [0, 1, 2, 3])
906 # Check with throw
907 class MyErr(Exception):
908 pass
909 def one():
910 try:
911 yield 0
912 except MyErr:
913 pass
914 yield from two()
915 try:
916 yield 3
917 except MyErr:
918 pass
919 def two():
920 try:
921 yield 1
922 except MyErr:
923 pass
924 try:
925 yield from g1
926 except ValueError:
927 pass
928 try:
929 yield 2
930 except MyErr:
931 pass
932 g1 = one()
933 res = [next(g1)]
934 try:
935 while True:
936 res.append(g1.throw(MyErr))
937 except StopIteration:
938 pass
939 # Check with close
940 class MyIt(object):
941 def __iter__(self):
942 return self
943 def __next__(self):
944 return 42
945 def close(self_):
946 self.assertTrue(g1.gi_running)
947 self.assertRaises(ValueError, next, g1)
948 def one():
949 yield from MyIt()
950 g1 = one()
951 next(g1)
952 g1.close()
953
Benjamin Peterson2afe6ae2012-03-15 15:37:39 -0500954 def test_delegator_is_visible_to_debugger(self):
955 def call_stack():
956 return [f[3] for f in inspect.stack()]
957
958 def gen():
959 yield call_stack()
960 yield call_stack()
961 yield call_stack()
962
963 def spam(g):
964 yield from g
965
966 def eggs(g):
967 yield from g
968
969 for stack in spam(gen()):
970 self.assertTrue('spam' in stack)
971
972 for stack in spam(eggs(gen())):
973 self.assertTrue('spam' in stack and 'eggs' in stack)
974
Benjamin Petersonb37df512012-08-06 17:53:09 -0700975 def test_custom_iterator_return(self):
976 # See issue #15568
977 class MyIter:
978 def __iter__(self):
979 return self
980 def __next__(self):
981 raise StopIteration(42)
982 def gen():
983 nonlocal ret
984 ret = yield from MyIter()
985 ret = None
986 list(gen())
987 self.assertEqual(ret, 42)
988
Benjamin Petersond979c722013-07-27 14:06:56 -0700989 def test_close_with_cleared_frame(self):
990 # See issue #17669.
991 #
992 # Create a stack of generators: outer() delegating to inner()
993 # delegating to innermost(). The key point is that the instance of
994 # inner is created first: this ensures that its frame appears before
995 # the instance of outer in the GC linked list.
996 #
997 # At the gc.collect call:
998 # - frame_clear is called on the inner_gen frame.
999 # - gen_dealloc is called on the outer_gen generator (the only
1000 # reference is in the frame's locals).
1001 # - gen_close is called on the outer_gen generator.
1002 # - gen_close_iter is called to close the inner_gen generator, which
1003 # in turn calls gen_close, and gen_yf.
1004 #
1005 # Previously, gen_yf would crash since inner_gen's frame had been
1006 # cleared (and in particular f_stacktop was NULL).
1007
1008 def innermost():
1009 yield
1010 def inner():
1011 outer_gen = yield
1012 yield from innermost()
1013 def outer():
1014 inner_gen = yield
1015 yield from inner_gen
1016
1017 with disable_gc():
1018 inner_gen = inner()
1019 outer_gen = outer()
1020 outer_gen.send(None)
1021 outer_gen.send(inner_gen)
1022 outer_gen.send(outer_gen)
1023
1024 del outer_gen
1025 del inner_gen
1026 gc_collect()
1027
Benjamin Petersonf6e50b42014-04-13 23:52:01 -04001028 def test_send_tuple_with_custom_generator(self):
1029 # See issue #21209.
1030 class MyGen:
1031 def __iter__(self):
1032 return self
1033 def __next__(self):
1034 return 42
1035 def send(self, what):
1036 nonlocal v
1037 v = what
1038 return None
1039 def outer():
1040 v = yield from MyGen()
1041 g = outer()
1042 next(g)
1043 v = None
1044 g.send((1, 2, 3, 4))
1045 self.assertEqual(v, (1, 2, 3, 4))
1046
Benjamin Peterson9a80fa82012-01-13 14:39:38 -05001047
Benjamin Peterson9a80fa82012-01-13 14:39:38 -05001048if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -05001049 unittest.main()