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