1# -*- 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
13import inspect
14import parser
15
16from test.support import captured_stderr, disable_gc, gc_collect
17
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
367    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
378    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 %r" % (ret,))
388            for v in 1, (2,), StopIteration(3):
389                ret = yield from g2(v)
390                trace.append("g2 returned %r" % (ret,))
391            yield "g1 eggs"
392            trace.append("Finishing g1")
393        def g2(v = None):
394            trace.append("Starting g2")
395            yield "g2 spam"
396            yield "g2 more spam"
397            trace.append("Finishing g2")
398            if v:
399                return v
400        for x in g1():
401            trace.append("Yielded %s" % (x,))
402        self.assertEqual(trace,[
403            "Starting g1",
404            "Yielded g1 ham",
405            "Starting g2",
406            "Yielded g2 spam",
407            "Yielded g2 more spam",
408            "Finishing g2",
409            "g2 returned None",
410            "Starting g2",
411            "Yielded g2 spam",
412            "Yielded g2 more spam",
413            "Finishing g2",
414            "g2 returned 1",
415            "Starting g2",
416            "Yielded g2 spam",
417            "Yielded g2 more spam",
418            "Finishing g2",
419            "g2 returned (2,)",
420            "Starting g2",
421            "Yielded g2 spam",
422            "Yielded g2 more spam",
423            "Finishing g2",
424            "g2 returned StopIteration(3,)",
425            "Yielded g1 eggs",
426            "Finishing g1",
427        ])
428
429    def test_delegation_of_next_to_non_generator(self):
430        """
431        Test delegation of next() to non-generator
432        """
433        trace = []
434        def g():
435            yield from range(3)
436        for x in g():
437            trace.append("Yielded %s" % (x,))
438        self.assertEqual(trace,[
439            "Yielded 0",
440            "Yielded 1",
441            "Yielded 2",
442        ])
443
444
445    def test_conversion_of_sendNone_to_next(self):
446        """
447        Test conversion of send(None) to next()
448        """
449        trace = []
450        def g():
451            yield from range(3)
452        gi = g()
453        for x in range(3):
454            y = gi.send(None)
455            trace.append("Yielded: %s" % (y,))
456        self.assertEqual(trace,[
457            "Yielded: 0",
458            "Yielded: 1",
459            "Yielded: 2",
460        ])
461
462    def test_delegation_of_close_to_non_generator(self):
463        """
464        Test delegation of close() to non-generator
465        """
466        trace = []
467        def g():
468            try:
469                trace.append("starting g")
470                yield from range(3)
471                trace.append("g should not be here")
472            finally:
473                trace.append("finishing g")
474        gi = g()
475        next(gi)
476        with captured_stderr() as output:
477            gi.close()
478        self.assertEqual(output.getvalue(), '')
479        self.assertEqual(trace,[
480            "starting g",
481            "finishing g",
482        ])
483
484    def test_delegating_throw_to_non_generator(self):
485        """
486        Test delegating 'throw' to non-generator
487        """
488        trace = []
489        def g():
490            try:
491                trace.append("Starting g")
492                yield from range(10)
493            finally:
494                trace.append("Finishing g")
495        try:
496            gi = g()
497            for i in range(5):
498                x = next(gi)
499                trace.append("Yielded %s" % (x,))
500            e = ValueError("tomato ejected")
501            gi.throw(e)
502        except ValueError as e:
503            self.assertEqual(e.args[0],"tomato ejected")
504        else:
505            self.fail("subgenerator failed to raise ValueError")
506        self.assertEqual(trace,[
507            "Starting g",
508            "Yielded 0",
509            "Yielded 1",
510            "Yielded 2",
511            "Yielded 3",
512            "Yielded 4",
513            "Finishing g",
514        ])
515
516    def test_attempting_to_send_to_non_generator(self):
517        """
518        Test attempting to send to non-generator
519        """
520        trace = []
521        def g():
522            try:
523                trace.append("starting g")
524                yield from range(3)
525                trace.append("g should not be here")
526            finally:
527                trace.append("finishing g")
528        try:
529            gi = g()
530            next(gi)
531            for x in range(3):
532                y = gi.send(42)
533                trace.append("Should not have yielded: %s" % (y,))
534        except AttributeError as e:
535            self.assertIn("send", e.args[0])
536        else:
537            self.fail("was able to send into non-generator")
538        self.assertEqual(trace,[
539            "starting g",
540            "finishing g",
541        ])
542
543    def test_broken_getattr_handling(self):
544        """
545        Test subiterator with a broken getattr implementation
546        """
547        class Broken:
548            def __iter__(self):
549                return self
550            def __next__(self):
551                return 1
552            def __getattr__(self, attr):
553                1/0
554
555        def g():
556            yield from Broken()
557
558        with self.assertRaises(ZeroDivisionError):
559            gi = g()
560            self.assertEqual(next(gi), 1)
561            gi.send(1)
562
563        with self.assertRaises(ZeroDivisionError):
564            gi = g()
565            self.assertEqual(next(gi), 1)
566            gi.throw(AttributeError)
567
568        with captured_stderr() as output:
569            gi = g()
570            self.assertEqual(next(gi), 1)
571            gi.close()
572        self.assertIn('ZeroDivisionError', output.getvalue())
573
574    def test_exception_in_initial_next_call(self):
575        """
576        Test exception in initial next() call
577        """
578        trace = []
579        def g1():
580            trace.append("g1 about to yield from g2")
581            yield from g2()
582            trace.append("g1 should not be here")
583        def g2():
584            yield 1/0
585        def run():
586            gi = g1()
587            next(gi)
588        self.assertRaises(ZeroDivisionError,run)
589        self.assertEqual(trace,[
590            "g1 about to yield from g2"
591        ])
592
593    def test_attempted_yield_from_loop(self):
594        """
595        Test attempted yield-from loop
596        """
597        trace = []
598        def g1():
599            trace.append("g1: starting")
600            yield "y1"
601            trace.append("g1: about to yield from g2")
602            yield from g2()
603            trace.append("g1 should not be here")
604
605        def g2():
606            trace.append("g2: starting")
607            yield "y2"
608            trace.append("g2: about to yield from g1")
609            yield from gi
610            trace.append("g2 should not be here")
611        try:
612            gi = g1()
613            for y in gi:
614                trace.append("Yielded: %s" % (y,))
615        except ValueError as e:
616            self.assertEqual(e.args[0],"generator already executing")
617        else:
618            self.fail("subgenerator didn't raise ValueError")
619        self.assertEqual(trace,[
620            "g1: starting",
621            "Yielded: y1",
622            "g1: about to yield from g2",
623            "g2: starting",
624            "Yielded: y2",
625            "g2: about to yield from g1",
626        ])
627
628    def test_returning_value_from_delegated_throw(self):
629        """
630        Test returning value from delegated 'throw'
631        """
632        trace = []
633        def g1():
634            try:
635                trace.append("Starting g1")
636                yield "g1 ham"
637                yield from g2()
638                yield "g1 eggs"
639            finally:
640                trace.append("Finishing g1")
641        def g2():
642            try:
643                trace.append("Starting g2")
644                yield "g2 spam"
645                yield "g2 more spam"
646            except LunchError:
647                trace.append("Caught LunchError in g2")
648                yield "g2 lunch saved"
649                yield "g2 yet more spam"
650        class LunchError(Exception):
651            pass
652        g = g1()
653        for i in range(2):
654            x = next(g)
655            trace.append("Yielded %s" % (x,))
656        e = LunchError("tomato ejected")
657        g.throw(e)
658        for x in g:
659            trace.append("Yielded %s" % (x,))
660        self.assertEqual(trace,[
661            "Starting g1",
662            "Yielded g1 ham",
663            "Starting g2",
664            "Yielded g2 spam",
665            "Caught LunchError in g2",
666            "Yielded g2 yet more spam",
667            "Yielded g1 eggs",
668            "Finishing g1",
669        ])
670
671    def test_next_and_return_with_value(self):
672        """
673        Test next and return with value
674        """
675        trace = []
676        def f(r):
677            gi = g(r)
678            next(gi)
679            try:
680                trace.append("f resuming g")
681                next(gi)
682                trace.append("f SHOULD NOT BE HERE")
683            except StopIteration as e:
684                trace.append("f caught %r" % (e,))
685        def g(r):
686            trace.append("g starting")
687            yield
688            trace.append("g returning %r" % (r,))
689            return r
690        f(None)
691        f(1)
692        f((2,))
693        f(StopIteration(3))
694        self.assertEqual(trace,[
695            "g starting",
696            "f resuming g",
697            "g returning None",
698            "f caught StopIteration()",
699            "g starting",
700            "f resuming g",
701            "g returning 1",
702            "f caught StopIteration(1,)",
703            "g starting",
704            "f resuming g",
705            "g returning (2,)",
706            "f caught StopIteration((2,),)",
707            "g starting",
708            "f resuming g",
709            "g returning StopIteration(3,)",
710            "f caught StopIteration(StopIteration(3,),)",
711        ])
712
713    def test_send_and_return_with_value(self):
714        """
715        Test send and return with value
716        """
717        trace = []
718        def f(r):
719            gi = g(r)
720            next(gi)
721            try:
722                trace.append("f sending spam to g")
723                gi.send("spam")
724                trace.append("f SHOULD NOT BE HERE")
725            except StopIteration as e:
726                trace.append("f caught %r" % (e,))
727        def g(r):
728            trace.append("g starting")
729            x = yield
730            trace.append("g received %r" % (x,))
731            trace.append("g returning %r" % (r,))
732            return r
733        f(None)
734        f(1)
735        f((2,))
736        f(StopIteration(3))
737        self.assertEqual(trace, [
738            "g starting",
739            "f sending spam to g",
740            "g received 'spam'",
741            "g returning None",
742            "f caught StopIteration()",
743            "g starting",
744            "f sending spam to g",
745            "g received 'spam'",
746            "g returning 1",
747            'f caught StopIteration(1,)',
748            'g starting',
749            'f sending spam to g',
750            "g received 'spam'",
751            'g returning (2,)',
752            'f caught StopIteration((2,),)',
753            'g starting',
754            'f sending spam to g',
755            "g received 'spam'",
756            'g returning StopIteration(3,)',
757            'f caught StopIteration(StopIteration(3,),)'
758        ])
759
760    def test_catching_exception_from_subgen_and_returning(self):
761        """
762        Test catching an exception thrown into a
763        subgenerator and returning a value
764        """
765        def inner():
766            try:
767                yield 1
768            except ValueError:
769                trace.append("inner caught ValueError")
770            return value
771
772        def outer():
773            v = yield from inner()
774            trace.append("inner returned %r to outer" % (v,))
775            yield v
776
777        for value in 2, (2,), StopIteration(2):
778            trace = []
779            g = outer()
780            trace.append(next(g))
781            trace.append(repr(g.throw(ValueError)))
782            self.assertEqual(trace, [
783                1,
784                "inner caught ValueError",
785                "inner returned %r to outer" % (value,),
786                repr(value),
787            ])
788
789    def test_throwing_GeneratorExit_into_subgen_that_returns(self):
790        """
791        Test throwing GeneratorExit into a subgenerator that
792        catches it and returns normally.
793        """
794        trace = []
795        def f():
796            try:
797                trace.append("Enter f")
798                yield
799                trace.append("Exit f")
800            except GeneratorExit:
801                return
802        def g():
803            trace.append("Enter g")
804            yield from f()
805            trace.append("Exit g")
806        try:
807            gi = g()
808            next(gi)
809            gi.throw(GeneratorExit)
810        except GeneratorExit:
811            pass
812        else:
813            self.fail("subgenerator failed to raise GeneratorExit")
814        self.assertEqual(trace,[
815            "Enter g",
816            "Enter f",
817        ])
818
819    def test_throwing_GeneratorExit_into_subgenerator_that_yields(self):
820        """
821        Test throwing GeneratorExit into a subgenerator that
822        catches it and yields.
823        """
824        trace = []
825        def f():
826            try:
827                trace.append("Enter f")
828                yield
829                trace.append("Exit f")
830            except GeneratorExit:
831                yield
832        def g():
833            trace.append("Enter g")
834            yield from f()
835            trace.append("Exit g")
836        try:
837            gi = g()
838            next(gi)
839            gi.throw(GeneratorExit)
840        except RuntimeError as e:
841            self.assertEqual(e.args[0], "generator ignored GeneratorExit")
842        else:
843            self.fail("subgenerator failed to raise GeneratorExit")
844        self.assertEqual(trace,[
845            "Enter g",
846            "Enter f",
847        ])
848
849    def test_throwing_GeneratorExit_into_subgen_that_raises(self):
850        """
851        Test throwing GeneratorExit into a subgenerator that
852        catches it and raises a different exception.
853        """
854        trace = []
855        def f():
856            try:
857                trace.append("Enter f")
858                yield
859                trace.append("Exit f")
860            except GeneratorExit:
861                raise ValueError("Vorpal bunny encountered")
862        def g():
863            trace.append("Enter g")
864            yield from f()
865            trace.append("Exit g")
866        try:
867            gi = g()
868            next(gi)
869            gi.throw(GeneratorExit)
870        except ValueError as e:
871            self.assertEqual(e.args[0], "Vorpal bunny encountered")
872            self.assertIsInstance(e.__context__, GeneratorExit)
873        else:
874            self.fail("subgenerator failed to raise ValueError")
875        self.assertEqual(trace,[
876            "Enter g",
877            "Enter f",
878        ])
879
880    def test_yield_from_empty(self):
881        def g():
882            yield from ()
883        self.assertRaises(StopIteration, next, g())
884
885    def test_delegating_generators_claim_to_be_running(self):
886        # Check with basic iteration
887        def one():
888            yield 0
889            yield from two()
890            yield 3
891        def two():
892            yield 1
893            try:
894                yield from g1
895            except ValueError:
896                pass
897            yield 2
898        g1 = one()
899        self.assertEqual(list(g1), [0, 1, 2, 3])
900        # Check with send
901        g1 = one()
902        res = [next(g1)]
903        try:
904            while True:
905                res.append(g1.send(42))
906        except StopIteration:
907            pass
908        self.assertEqual(res, [0, 1, 2, 3])
909        # Check with throw
910        class MyErr(Exception):
911            pass
912        def one():
913            try:
914                yield 0
915            except MyErr:
916                pass
917            yield from two()
918            try:
919                yield 3
920            except MyErr:
921                pass
922        def two():
923            try:
924                yield 1
925            except MyErr:
926                pass
927            try:
928                yield from g1
929            except ValueError:
930                pass
931            try:
932                yield 2
933            except MyErr:
934                pass
935        g1 = one()
936        res = [next(g1)]
937        try:
938            while True:
939                res.append(g1.throw(MyErr))
940        except StopIteration:
941            pass
942        # Check with close
943        class MyIt(object):
944            def __iter__(self):
945                return self
946            def __next__(self):
947                return 42
948            def close(self_):
949                self.assertTrue(g1.gi_running)
950                self.assertRaises(ValueError, next, g1)
951        def one():
952            yield from MyIt()
953        g1 = one()
954        next(g1)
955        g1.close()
956
957    def test_delegator_is_visible_to_debugger(self):
958        def call_stack():
959            return [f[3] for f in inspect.stack()]
960
961        def gen():
962            yield call_stack()
963            yield call_stack()
964            yield call_stack()
965
966        def spam(g):
967            yield from g
968
969        def eggs(g):
970            yield from g
971
972        for stack in spam(gen()):
973            self.assertTrue('spam' in stack)
974
975        for stack in spam(eggs(gen())):
976            self.assertTrue('spam' in stack and 'eggs' in stack)
977
978    def test_custom_iterator_return(self):
979        # See issue #15568
980        class MyIter:
981            def __iter__(self):
982                return self
983            def __next__(self):
984                raise StopIteration(42)
985        def gen():
986            nonlocal ret
987            ret = yield from MyIter()
988        ret = None
989        list(gen())
990        self.assertEqual(ret, 42)
991
992    def test_close_with_cleared_frame(self):
993        # See issue #17669.
994        #
995        # Create a stack of generators: outer() delegating to inner()
996        # delegating to innermost(). The key point is that the instance of
997        # inner is created first: this ensures that its frame appears before
998        # the instance of outer in the GC linked list.
999        #
1000        # At the gc.collect call:
1001        #   - frame_clear is called on the inner_gen frame.
1002        #   - gen_dealloc is called on the outer_gen generator (the only
1003        #     reference is in the frame's locals).
1004        #   - gen_close is called on the outer_gen generator.
1005        #   - gen_close_iter is called to close the inner_gen generator, which
1006        #     in turn calls gen_close, and gen_yf.
1007        #
1008        # Previously, gen_yf would crash since inner_gen's frame had been
1009        # cleared (and in particular f_stacktop was NULL).
1010
1011        def innermost():
1012            yield
1013        def inner():
1014            outer_gen = yield
1015            yield from innermost()
1016        def outer():
1017            inner_gen = yield
1018            yield from inner_gen
1019
1020        with disable_gc():
1021            inner_gen = inner()
1022            outer_gen = outer()
1023            outer_gen.send(None)
1024            outer_gen.send(inner_gen)
1025            outer_gen.send(outer_gen)
1026
1027            del outer_gen
1028            del inner_gen
1029            gc_collect()
1030
1031    def test_send_tuple_with_custom_generator(self):
1032        # See issue #21209.
1033        class MyGen:
1034            def __iter__(self):
1035                return self
1036            def __next__(self):
1037                return 42
1038            def send(self, what):
1039                nonlocal v
1040                v = what
1041                return None
1042        def outer():
1043            v = yield from MyGen()
1044        g = outer()
1045        next(g)
1046        v = None
1047        g.send((1, 2, 3, 4))
1048        self.assertEqual(v, (1, 2, 3, 4))
1049
1050
1051if __name__ == '__main__':
1052    unittest.main()
1053