Home | History | Annotate | Download | only in test
      1 # -*- coding: utf-8 -*-
      2 
      3 """
      4 Test suite for PEP 380 implementation
      5 
      6 adapted from original tests written by Greg Ewing
      7 see <http://www.cosc.canterbury.ac.nz/greg.ewing/python/yield-from/YieldFrom-Python3.1.2-rev5.zip>
      8 """
      9 
     10 import unittest
     11 import io
     12 import sys
     13 import inspect
     14 import parser
     15 
     16 from test.support import captured_stderr, disable_gc, gc_collect
     17 
     18 class 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 
   1051 if __name__ == '__main__':
   1052     unittest.main()
   1053