(root)/
Python-3.12.0/
Lib/
test/
test_yield_from.py
       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 inspect
      12  
      13  from test.support import captured_stderr, disable_gc, gc_collect
      14  from test import support
      15  
      16  class ESC[4;38;5;81mTestPEP380Operation(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      17      """
      18      Test semantics.
      19      """
      20  
      21      def test_delegation_of_initial_next_to_subgenerator(self):
      22          """
      23          Test delegation of initial next() call to subgenerator
      24          """
      25          trace = []
      26          def g1():
      27              trace.append("Starting g1")
      28              yield from g2()
      29              trace.append("Finishing g1")
      30          def g2():
      31              trace.append("Starting g2")
      32              yield 42
      33              trace.append("Finishing g2")
      34          for x in g1():
      35              trace.append("Yielded %s" % (x,))
      36          self.assertEqual(trace,[
      37              "Starting g1",
      38              "Starting g2",
      39              "Yielded 42",
      40              "Finishing g2",
      41              "Finishing g1",
      42          ])
      43  
      44      def test_raising_exception_in_initial_next_call(self):
      45          """
      46          Test raising exception in initial next() call
      47          """
      48          trace = []
      49          def g1():
      50              try:
      51                  trace.append("Starting g1")
      52                  yield from g2()
      53              finally:
      54                  trace.append("Finishing g1")
      55          def g2():
      56              try:
      57                  trace.append("Starting g2")
      58                  raise ValueError("spanish inquisition occurred")
      59              finally:
      60                  trace.append("Finishing g2")
      61          try:
      62              for x in g1():
      63                  trace.append("Yielded %s" % (x,))
      64          except ValueError as e:
      65              self.assertEqual(e.args[0], "spanish inquisition occurred")
      66          else:
      67              self.fail("subgenerator failed to raise ValueError")
      68          self.assertEqual(trace,[
      69              "Starting g1",
      70              "Starting g2",
      71              "Finishing g2",
      72              "Finishing g1",
      73          ])
      74  
      75      def test_delegation_of_next_call_to_subgenerator(self):
      76          """
      77          Test delegation of next() call to subgenerator
      78          """
      79          trace = []
      80          def g1():
      81              trace.append("Starting g1")
      82              yield "g1 ham"
      83              yield from g2()
      84              yield "g1 eggs"
      85              trace.append("Finishing g1")
      86          def g2():
      87              trace.append("Starting g2")
      88              yield "g2 spam"
      89              yield "g2 more spam"
      90              trace.append("Finishing g2")
      91          for x in g1():
      92              trace.append("Yielded %s" % (x,))
      93          self.assertEqual(trace,[
      94              "Starting g1",
      95              "Yielded g1 ham",
      96              "Starting g2",
      97              "Yielded g2 spam",
      98              "Yielded g2 more spam",
      99              "Finishing g2",
     100              "Yielded g1 eggs",
     101              "Finishing g1",
     102          ])
     103  
     104      def test_raising_exception_in_delegated_next_call(self):
     105          """
     106          Test raising exception in delegated next() call
     107          """
     108          trace = []
     109          def g1():
     110              try:
     111                  trace.append("Starting g1")
     112                  yield "g1 ham"
     113                  yield from g2()
     114                  yield "g1 eggs"
     115              finally:
     116                  trace.append("Finishing g1")
     117          def g2():
     118              try:
     119                  trace.append("Starting g2")
     120                  yield "g2 spam"
     121                  raise ValueError("hovercraft is full of eels")
     122                  yield "g2 more spam"
     123              finally:
     124                  trace.append("Finishing g2")
     125          try:
     126              for x in g1():
     127                  trace.append("Yielded %s" % (x,))
     128          except ValueError as e:
     129              self.assertEqual(e.args[0], "hovercraft is full of eels")
     130          else:
     131              self.fail("subgenerator failed to raise ValueError")
     132          self.assertEqual(trace,[
     133              "Starting g1",
     134              "Yielded g1 ham",
     135              "Starting g2",
     136              "Yielded g2 spam",
     137              "Finishing g2",
     138              "Finishing g1",
     139          ])
     140  
     141      def test_delegation_of_send(self):
     142          """
     143          Test delegation of send()
     144          """
     145          trace = []
     146          def g1():
     147              trace.append("Starting g1")
     148              x = yield "g1 ham"
     149              trace.append("g1 received %s" % (x,))
     150              yield from g2()
     151              x = yield "g1 eggs"
     152              trace.append("g1 received %s" % (x,))
     153              trace.append("Finishing g1")
     154          def g2():
     155              trace.append("Starting g2")
     156              x = yield "g2 spam"
     157              trace.append("g2 received %s" % (x,))
     158              x = yield "g2 more spam"
     159              trace.append("g2 received %s" % (x,))
     160              trace.append("Finishing g2")
     161          g = g1()
     162          y = next(g)
     163          x = 1
     164          try:
     165              while 1:
     166                  y = g.send(x)
     167                  trace.append("Yielded %s" % (y,))
     168                  x += 1
     169          except StopIteration:
     170              pass
     171          self.assertEqual(trace,[
     172              "Starting g1",
     173              "g1 received 1",
     174              "Starting g2",
     175              "Yielded g2 spam",
     176              "g2 received 2",
     177              "Yielded g2 more spam",
     178              "g2 received 3",
     179              "Finishing g2",
     180              "Yielded g1 eggs",
     181              "g1 received 4",
     182              "Finishing g1",
     183          ])
     184  
     185      def test_handling_exception_while_delegating_send(self):
     186          """
     187          Test handling exception while delegating 'send'
     188          """
     189          trace = []
     190          def g1():
     191              trace.append("Starting g1")
     192              x = yield "g1 ham"
     193              trace.append("g1 received %s" % (x,))
     194              yield from g2()
     195              x = yield "g1 eggs"
     196              trace.append("g1 received %s" % (x,))
     197              trace.append("Finishing g1")
     198          def g2():
     199              trace.append("Starting g2")
     200              x = yield "g2 spam"
     201              trace.append("g2 received %s" % (x,))
     202              raise ValueError("hovercraft is full of eels")
     203              x = yield "g2 more spam"
     204              trace.append("g2 received %s" % (x,))
     205              trace.append("Finishing g2")
     206          def run():
     207              g = g1()
     208              y = next(g)
     209              x = 1
     210              try:
     211                  while 1:
     212                      y = g.send(x)
     213                      trace.append("Yielded %s" % (y,))
     214                      x += 1
     215              except StopIteration:
     216                  trace.append("StopIteration")
     217          self.assertRaises(ValueError,run)
     218          self.assertEqual(trace,[
     219              "Starting g1",
     220              "g1 received 1",
     221              "Starting g2",
     222              "Yielded g2 spam",
     223              "g2 received 2",
     224          ])
     225  
     226      def test_delegating_close(self):
     227          """
     228          Test delegating 'close'
     229          """
     230          trace = []
     231          def g1():
     232              try:
     233                  trace.append("Starting g1")
     234                  yield "g1 ham"
     235                  yield from g2()
     236                  yield "g1 eggs"
     237              finally:
     238                  trace.append("Finishing g1")
     239          def g2():
     240              try:
     241                  trace.append("Starting g2")
     242                  yield "g2 spam"
     243                  yield "g2 more spam"
     244              finally:
     245                  trace.append("Finishing g2")
     246          g = g1()
     247          for i in range(2):
     248              x = next(g)
     249              trace.append("Yielded %s" % (x,))
     250          g.close()
     251          self.assertEqual(trace,[
     252              "Starting g1",
     253              "Yielded g1 ham",
     254              "Starting g2",
     255              "Yielded g2 spam",
     256              "Finishing g2",
     257              "Finishing g1"
     258          ])
     259  
     260      def test_handing_exception_while_delegating_close(self):
     261          """
     262          Test handling exception while delegating 'close'
     263          """
     264          trace = []
     265          def g1():
     266              try:
     267                  trace.append("Starting g1")
     268                  yield "g1 ham"
     269                  yield from g2()
     270                  yield "g1 eggs"
     271              finally:
     272                  trace.append("Finishing g1")
     273          def g2():
     274              try:
     275                  trace.append("Starting g2")
     276                  yield "g2 spam"
     277                  yield "g2 more spam"
     278              finally:
     279                  trace.append("Finishing g2")
     280                  raise ValueError("nybbles have exploded with delight")
     281          try:
     282              g = g1()
     283              for i in range(2):
     284                  x = next(g)
     285                  trace.append("Yielded %s" % (x,))
     286              g.close()
     287          except ValueError as e:
     288              self.assertEqual(e.args[0], "nybbles have exploded with delight")
     289              self.assertIsInstance(e.__context__, GeneratorExit)
     290          else:
     291              self.fail("subgenerator failed to raise ValueError")
     292          self.assertEqual(trace,[
     293              "Starting g1",
     294              "Yielded g1 ham",
     295              "Starting g2",
     296              "Yielded g2 spam",
     297              "Finishing g2",
     298              "Finishing g1",
     299          ])
     300  
     301      def test_delegating_throw(self):
     302          """
     303          Test delegating 'throw'
     304          """
     305          trace = []
     306          def g1():
     307              try:
     308                  trace.append("Starting g1")
     309                  yield "g1 ham"
     310                  yield from g2()
     311                  yield "g1 eggs"
     312              finally:
     313                  trace.append("Finishing g1")
     314          def g2():
     315              try:
     316                  trace.append("Starting g2")
     317                  yield "g2 spam"
     318                  yield "g2 more spam"
     319              finally:
     320                  trace.append("Finishing g2")
     321          try:
     322              g = g1()
     323              for i in range(2):
     324                  x = next(g)
     325                  trace.append("Yielded %s" % (x,))
     326              e = ValueError("tomato ejected")
     327              g.throw(e)
     328          except ValueError as e:
     329              self.assertEqual(e.args[0], "tomato ejected")
     330          else:
     331              self.fail("subgenerator failed to raise ValueError")
     332          self.assertEqual(trace,[
     333              "Starting g1",
     334              "Yielded g1 ham",
     335              "Starting g2",
     336              "Yielded g2 spam",
     337              "Finishing g2",
     338              "Finishing g1",
     339          ])
     340  
     341      def test_value_attribute_of_StopIteration_exception(self):
     342          """
     343          Test 'value' attribute of StopIteration exception
     344          """
     345          trace = []
     346          def pex(e):
     347              trace.append("%s: %s" % (e.__class__.__name__, e))
     348              trace.append("value = %s" % (e.value,))
     349          e = StopIteration()
     350          pex(e)
     351          e = StopIteration("spam")
     352          pex(e)
     353          e.value = "eggs"
     354          pex(e)
     355          self.assertEqual(trace,[
     356              "StopIteration: ",
     357              "value = None",
     358              "StopIteration: spam",
     359              "value = spam",
     360              "StopIteration: spam",
     361              "value = eggs",
     362          ])
     363  
     364  
     365      def test_exception_value_crash(self):
     366          # There used to be a refcount error when the return value
     367          # stored in the StopIteration has a refcount of 1.
     368          def g1():
     369              yield from g2()
     370          def g2():
     371              yield "g2"
     372              return [42]
     373          self.assertEqual(list(g1()), ["g2"])
     374  
     375  
     376      def test_generator_return_value(self):
     377          """
     378          Test generator return value
     379          """
     380          trace = []
     381          def g1():
     382              trace.append("Starting g1")
     383              yield "g1 ham"
     384              ret = yield from g2()
     385              trace.append("g2 returned %r" % (ret,))
     386              for v in 1, (2,), StopIteration(3):
     387                  ret = yield from g2(v)
     388                  trace.append("g2 returned %r" % (ret,))
     389              yield "g1 eggs"
     390              trace.append("Finishing g1")
     391          def g2(v = None):
     392              trace.append("Starting g2")
     393              yield "g2 spam"
     394              yield "g2 more spam"
     395              trace.append("Finishing g2")
     396              if v:
     397                  return v
     398          for x in g1():
     399              trace.append("Yielded %s" % (x,))
     400          self.assertEqual(trace,[
     401              "Starting g1",
     402              "Yielded g1 ham",
     403              "Starting g2",
     404              "Yielded g2 spam",
     405              "Yielded g2 more spam",
     406              "Finishing g2",
     407              "g2 returned None",
     408              "Starting g2",
     409              "Yielded g2 spam",
     410              "Yielded g2 more spam",
     411              "Finishing g2",
     412              "g2 returned 1",
     413              "Starting g2",
     414              "Yielded g2 spam",
     415              "Yielded g2 more spam",
     416              "Finishing g2",
     417              "g2 returned (2,)",
     418              "Starting g2",
     419              "Yielded g2 spam",
     420              "Yielded g2 more spam",
     421              "Finishing g2",
     422              "g2 returned StopIteration(3)",
     423              "Yielded g1 eggs",
     424              "Finishing g1",
     425          ])
     426  
     427      def test_delegation_of_next_to_non_generator(self):
     428          """
     429          Test delegation of next() to non-generator
     430          """
     431          trace = []
     432          def g():
     433              yield from range(3)
     434          for x in g():
     435              trace.append("Yielded %s" % (x,))
     436          self.assertEqual(trace,[
     437              "Yielded 0",
     438              "Yielded 1",
     439              "Yielded 2",
     440          ])
     441  
     442  
     443      def test_conversion_of_sendNone_to_next(self):
     444          """
     445          Test conversion of send(None) to next()
     446          """
     447          trace = []
     448          def g():
     449              yield from range(3)
     450          gi = g()
     451          for x in range(3):
     452              y = gi.send(None)
     453              trace.append("Yielded: %s" % (y,))
     454          self.assertEqual(trace,[
     455              "Yielded: 0",
     456              "Yielded: 1",
     457              "Yielded: 2",
     458          ])
     459  
     460      def test_delegation_of_close_to_non_generator(self):
     461          """
     462          Test delegation of close() to non-generator
     463          """
     464          trace = []
     465          def g():
     466              try:
     467                  trace.append("starting g")
     468                  yield from range(3)
     469                  trace.append("g should not be here")
     470              finally:
     471                  trace.append("finishing g")
     472          gi = g()
     473          next(gi)
     474          with captured_stderr() as output:
     475              gi.close()
     476          self.assertEqual(output.getvalue(), '')
     477          self.assertEqual(trace,[
     478              "starting g",
     479              "finishing g",
     480          ])
     481  
     482      def test_delegating_throw_to_non_generator(self):
     483          """
     484          Test delegating 'throw' to non-generator
     485          """
     486          trace = []
     487          def g():
     488              try:
     489                  trace.append("Starting g")
     490                  yield from range(10)
     491              finally:
     492                  trace.append("Finishing g")
     493          try:
     494              gi = g()
     495              for i in range(5):
     496                  x = next(gi)
     497                  trace.append("Yielded %s" % (x,))
     498              e = ValueError("tomato ejected")
     499              gi.throw(e)
     500          except ValueError as e:
     501              self.assertEqual(e.args[0],"tomato ejected")
     502          else:
     503              self.fail("subgenerator failed to raise ValueError")
     504          self.assertEqual(trace,[
     505              "Starting g",
     506              "Yielded 0",
     507              "Yielded 1",
     508              "Yielded 2",
     509              "Yielded 3",
     510              "Yielded 4",
     511              "Finishing g",
     512          ])
     513  
     514      def test_attempting_to_send_to_non_generator(self):
     515          """
     516          Test attempting to send to non-generator
     517          """
     518          trace = []
     519          def g():
     520              try:
     521                  trace.append("starting g")
     522                  yield from range(3)
     523                  trace.append("g should not be here")
     524              finally:
     525                  trace.append("finishing g")
     526          try:
     527              gi = g()
     528              next(gi)
     529              for x in range(3):
     530                  y = gi.send(42)
     531                  trace.append("Should not have yielded: %s" % (y,))
     532          except AttributeError as e:
     533              self.assertIn("send", e.args[0])
     534          else:
     535              self.fail("was able to send into non-generator")
     536          self.assertEqual(trace,[
     537              "starting g",
     538              "finishing g",
     539          ])
     540  
     541      def test_broken_getattr_handling(self):
     542          """
     543          Test subiterator with a broken getattr implementation
     544          """
     545          class ESC[4;38;5;81mBroken:
     546              def __iter__(self):
     547                  return self
     548              def __next__(self):
     549                  return 1
     550              def __getattr__(self, attr):
     551                  1/0
     552  
     553          def g():
     554              yield from Broken()
     555  
     556          with self.assertRaises(ZeroDivisionError):
     557              gi = g()
     558              self.assertEqual(next(gi), 1)
     559              gi.send(1)
     560  
     561          with self.assertRaises(ZeroDivisionError):
     562              gi = g()
     563              self.assertEqual(next(gi), 1)
     564              gi.throw(AttributeError)
     565  
     566          with support.catch_unraisable_exception() as cm:
     567              gi = g()
     568              self.assertEqual(next(gi), 1)
     569              gi.close()
     570  
     571              self.assertEqual(ZeroDivisionError, cm.unraisable.exc_type)
     572  
     573      def test_exception_in_initial_next_call(self):
     574          """
     575          Test exception in initial next() call
     576          """
     577          trace = []
     578          def g1():
     579              trace.append("g1 about to yield from g2")
     580              yield from g2()
     581              trace.append("g1 should not be here")
     582          def g2():
     583              yield 1/0
     584          def run():
     585              gi = g1()
     586              next(gi)
     587          self.assertRaises(ZeroDivisionError,run)
     588          self.assertEqual(trace,[
     589              "g1 about to yield from g2"
     590          ])
     591  
     592      def test_attempted_yield_from_loop(self):
     593          """
     594          Test attempted yield-from loop
     595          """
     596          trace = []
     597          def g1():
     598              trace.append("g1: starting")
     599              yield "y1"
     600              trace.append("g1: about to yield from g2")
     601              yield from g2()
     602              trace.append("g1 should not be here")
     603  
     604          def g2():
     605              trace.append("g2: starting")
     606              yield "y2"
     607              trace.append("g2: about to yield from g1")
     608              yield from gi
     609              trace.append("g2 should not be here")
     610          try:
     611              gi = g1()
     612              for y in gi:
     613                  trace.append("Yielded: %s" % (y,))
     614          except ValueError as e:
     615              self.assertEqual(e.args[0],"generator already executing")
     616          else:
     617              self.fail("subgenerator didn't raise ValueError")
     618          self.assertEqual(trace,[
     619              "g1: starting",
     620              "Yielded: y1",
     621              "g1: about to yield from g2",
     622              "g2: starting",
     623              "Yielded: y2",
     624              "g2: about to yield from g1",
     625          ])
     626  
     627      def test_returning_value_from_delegated_throw(self):
     628          """
     629          Test returning value from delegated 'throw'
     630          """
     631          trace = []
     632          def g1():
     633              try:
     634                  trace.append("Starting g1")
     635                  yield "g1 ham"
     636                  yield from g2()
     637                  yield "g1 eggs"
     638              finally:
     639                  trace.append("Finishing g1")
     640          def g2():
     641              try:
     642                  trace.append("Starting g2")
     643                  yield "g2 spam"
     644                  yield "g2 more spam"
     645              except LunchError:
     646                  trace.append("Caught LunchError in g2")
     647                  yield "g2 lunch saved"
     648                  yield "g2 yet more spam"
     649          class ESC[4;38;5;81mLunchError(ESC[4;38;5;149mException):
     650              pass
     651          g = g1()
     652          for i in range(2):
     653              x = next(g)
     654              trace.append("Yielded %s" % (x,))
     655          e = LunchError("tomato ejected")
     656          g.throw(e)
     657          for x in g:
     658              trace.append("Yielded %s" % (x,))
     659          self.assertEqual(trace,[
     660              "Starting g1",
     661              "Yielded g1 ham",
     662              "Starting g2",
     663              "Yielded g2 spam",
     664              "Caught LunchError in g2",
     665              "Yielded g2 yet more spam",
     666              "Yielded g1 eggs",
     667              "Finishing g1",
     668          ])
     669  
     670      def test_next_and_return_with_value(self):
     671          """
     672          Test next and return with value
     673          """
     674          trace = []
     675          def f(r):
     676              gi = g(r)
     677              next(gi)
     678              try:
     679                  trace.append("f resuming g")
     680                  next(gi)
     681                  trace.append("f SHOULD NOT BE HERE")
     682              except StopIteration as e:
     683                  trace.append("f caught %r" % (e,))
     684          def g(r):
     685              trace.append("g starting")
     686              yield
     687              trace.append("g returning %r" % (r,))
     688              return r
     689          f(None)
     690          f(1)
     691          f((2,))
     692          f(StopIteration(3))
     693          self.assertEqual(trace,[
     694              "g starting",
     695              "f resuming g",
     696              "g returning None",
     697              "f caught StopIteration()",
     698              "g starting",
     699              "f resuming g",
     700              "g returning 1",
     701              "f caught StopIteration(1)",
     702              "g starting",
     703              "f resuming g",
     704              "g returning (2,)",
     705              "f caught StopIteration((2,))",
     706              "g starting",
     707              "f resuming g",
     708              "g returning StopIteration(3)",
     709              "f caught StopIteration(StopIteration(3))",
     710          ])
     711  
     712      def test_send_and_return_with_value(self):
     713          """
     714          Test send and return with value
     715          """
     716          trace = []
     717          def f(r):
     718              gi = g(r)
     719              next(gi)
     720              try:
     721                  trace.append("f sending spam to g")
     722                  gi.send("spam")
     723                  trace.append("f SHOULD NOT BE HERE")
     724              except StopIteration as e:
     725                  trace.append("f caught %r" % (e,))
     726          def g(r):
     727              trace.append("g starting")
     728              x = yield
     729              trace.append("g received %r" % (x,))
     730              trace.append("g returning %r" % (r,))
     731              return r
     732          f(None)
     733          f(1)
     734          f((2,))
     735          f(StopIteration(3))
     736          self.assertEqual(trace, [
     737              "g starting",
     738              "f sending spam to g",
     739              "g received 'spam'",
     740              "g returning None",
     741              "f caught StopIteration()",
     742              "g starting",
     743              "f sending spam to g",
     744              "g received 'spam'",
     745              "g returning 1",
     746              'f caught StopIteration(1)',
     747              'g starting',
     748              'f sending spam to g',
     749              "g received 'spam'",
     750              'g returning (2,)',
     751              'f caught StopIteration((2,))',
     752              'g starting',
     753              'f sending spam to g',
     754              "g received 'spam'",
     755              'g returning StopIteration(3)',
     756              'f caught StopIteration(StopIteration(3))'
     757          ])
     758  
     759      def test_catching_exception_from_subgen_and_returning(self):
     760          """
     761          Test catching an exception thrown into a
     762          subgenerator and returning a value
     763          """
     764          def inner():
     765              try:
     766                  yield 1
     767              except ValueError:
     768                  trace.append("inner caught ValueError")
     769              return value
     770  
     771          def outer():
     772              v = yield from inner()
     773              trace.append("inner returned %r to outer" % (v,))
     774              yield v
     775  
     776          for value in 2, (2,), StopIteration(2):
     777              trace = []
     778              g = outer()
     779              trace.append(next(g))
     780              trace.append(repr(g.throw(ValueError)))
     781              self.assertEqual(trace, [
     782                  1,
     783                  "inner caught ValueError",
     784                  "inner returned %r to outer" % (value,),
     785                  repr(value),
     786              ])
     787  
     788      def test_throwing_GeneratorExit_into_subgen_that_returns(self):
     789          """
     790          Test throwing GeneratorExit into a subgenerator that
     791          catches it and returns normally.
     792          """
     793          trace = []
     794          def f():
     795              try:
     796                  trace.append("Enter f")
     797                  yield
     798                  trace.append("Exit f")
     799              except GeneratorExit:
     800                  return
     801          def g():
     802              trace.append("Enter g")
     803              yield from f()
     804              trace.append("Exit g")
     805          try:
     806              gi = g()
     807              next(gi)
     808              gi.throw(GeneratorExit)
     809          except GeneratorExit:
     810              pass
     811          else:
     812              self.fail("subgenerator failed to raise GeneratorExit")
     813          self.assertEqual(trace,[
     814              "Enter g",
     815              "Enter f",
     816          ])
     817  
     818      def test_throwing_GeneratorExit_into_subgenerator_that_yields(self):
     819          """
     820          Test throwing GeneratorExit into a subgenerator that
     821          catches it and yields.
     822          """
     823          trace = []
     824          def f():
     825              try:
     826                  trace.append("Enter f")
     827                  yield
     828                  trace.append("Exit f")
     829              except GeneratorExit:
     830                  yield
     831          def g():
     832              trace.append("Enter g")
     833              yield from f()
     834              trace.append("Exit g")
     835          try:
     836              gi = g()
     837              next(gi)
     838              gi.throw(GeneratorExit)
     839          except RuntimeError as e:
     840              self.assertEqual(e.args[0], "generator ignored GeneratorExit")
     841          else:
     842              self.fail("subgenerator failed to raise GeneratorExit")
     843          self.assertEqual(trace,[
     844              "Enter g",
     845              "Enter f",
     846          ])
     847  
     848      def test_throwing_GeneratorExit_into_subgen_that_raises(self):
     849          """
     850          Test throwing GeneratorExit into a subgenerator that
     851          catches it and raises a different exception.
     852          """
     853          trace = []
     854          def f():
     855              try:
     856                  trace.append("Enter f")
     857                  yield
     858                  trace.append("Exit f")
     859              except GeneratorExit:
     860                  raise ValueError("Vorpal bunny encountered")
     861          def g():
     862              trace.append("Enter g")
     863              yield from f()
     864              trace.append("Exit g")
     865          try:
     866              gi = g()
     867              next(gi)
     868              gi.throw(GeneratorExit)
     869          except ValueError as e:
     870              self.assertEqual(e.args[0], "Vorpal bunny encountered")
     871              self.assertIsInstance(e.__context__, GeneratorExit)
     872          else:
     873              self.fail("subgenerator failed to raise ValueError")
     874          self.assertEqual(trace,[
     875              "Enter g",
     876              "Enter f",
     877          ])
     878  
     879      def test_yield_from_empty(self):
     880          def g():
     881              yield from ()
     882          self.assertRaises(StopIteration, next, g())
     883  
     884      def test_delegating_generators_claim_to_be_running(self):
     885          # Check with basic iteration
     886          def one():
     887              yield 0
     888              yield from two()
     889              yield 3
     890          def two():
     891              yield 1
     892              try:
     893                  yield from g1
     894              except ValueError:
     895                  pass
     896              yield 2
     897          g1 = one()
     898          self.assertEqual(list(g1), [0, 1, 2, 3])
     899          # Check with send
     900          g1 = one()
     901          res = [next(g1)]
     902          try:
     903              while True:
     904                  res.append(g1.send(42))
     905          except StopIteration:
     906              pass
     907          self.assertEqual(res, [0, 1, 2, 3])
     908          # Check with throw
     909          class ESC[4;38;5;81mMyErr(ESC[4;38;5;149mException):
     910              pass
     911          def one():
     912              try:
     913                  yield 0
     914              except MyErr:
     915                  pass
     916              yield from two()
     917              try:
     918                  yield 3
     919              except MyErr:
     920                  pass
     921          def two():
     922              try:
     923                  yield 1
     924              except MyErr:
     925                  pass
     926              try:
     927                  yield from g1
     928              except ValueError:
     929                  pass
     930              try:
     931                  yield 2
     932              except MyErr:
     933                  pass
     934          g1 = one()
     935          res = [next(g1)]
     936          try:
     937              while True:
     938                  res.append(g1.throw(MyErr))
     939          except StopIteration:
     940              pass
     941          except:
     942              self.assertEqual(res, [0, 1, 2, 3])
     943              raise
     944          # Check with close
     945          class ESC[4;38;5;81mMyIt(ESC[4;38;5;149mobject):
     946              def __iter__(self):
     947                  return self
     948              def __next__(self):
     949                  return 42
     950              def close(self_):
     951                  self.assertTrue(g1.gi_running)
     952                  self.assertRaises(ValueError, next, g1)
     953          def one():
     954              yield from MyIt()
     955          g1 = one()
     956          next(g1)
     957          g1.close()
     958  
     959      def test_delegator_is_visible_to_debugger(self):
     960          def call_stack():
     961              return [f[3] for f in inspect.stack()]
     962  
     963          def gen():
     964              yield call_stack()
     965              yield call_stack()
     966              yield call_stack()
     967  
     968          def spam(g):
     969              yield from g
     970  
     971          def eggs(g):
     972              yield from g
     973  
     974          for stack in spam(gen()):
     975              self.assertTrue('spam' in stack)
     976  
     977          for stack in spam(eggs(gen())):
     978              self.assertTrue('spam' in stack and 'eggs' in stack)
     979  
     980      def test_custom_iterator_return(self):
     981          # See issue #15568
     982          class ESC[4;38;5;81mMyIter:
     983              def __iter__(self):
     984                  return self
     985              def __next__(self):
     986                  raise StopIteration(42)
     987          def gen():
     988              nonlocal ret
     989              ret = yield from MyIter()
     990          ret = None
     991          list(gen())
     992          self.assertEqual(ret, 42)
     993  
     994      def test_close_with_cleared_frame(self):
     995          # See issue #17669.
     996          #
     997          # Create a stack of generators: outer() delegating to inner()
     998          # delegating to innermost(). The key point is that the instance of
     999          # inner is created first: this ensures that its frame appears before
    1000          # the instance of outer in the GC linked list.
    1001          #
    1002          # At the gc.collect call:
    1003          #   - frame_clear is called on the inner_gen frame.
    1004          #   - gen_dealloc is called on the outer_gen generator (the only
    1005          #     reference is in the frame's locals).
    1006          #   - gen_close is called on the outer_gen generator.
    1007          #   - gen_close_iter is called to close the inner_gen generator, which
    1008          #     in turn calls gen_close, and gen_yf.
    1009          #
    1010          # Previously, gen_yf would crash since inner_gen's frame had been
    1011          # cleared (and in particular f_stacktop was NULL).
    1012  
    1013          def innermost():
    1014              yield
    1015          def inner():
    1016              outer_gen = yield
    1017              yield from innermost()
    1018          def outer():
    1019              inner_gen = yield
    1020              yield from inner_gen
    1021  
    1022          with disable_gc():
    1023              inner_gen = inner()
    1024              outer_gen = outer()
    1025              outer_gen.send(None)
    1026              outer_gen.send(inner_gen)
    1027              outer_gen.send(outer_gen)
    1028  
    1029              del outer_gen
    1030              del inner_gen
    1031              gc_collect()
    1032  
    1033      def test_send_tuple_with_custom_generator(self):
    1034          # See issue #21209.
    1035          class ESC[4;38;5;81mMyGen:
    1036              def __iter__(self):
    1037                  return self
    1038              def __next__(self):
    1039                  return 42
    1040              def send(self, what):
    1041                  nonlocal v
    1042                  v = what
    1043                  return None
    1044          def outer():
    1045              v = yield from MyGen()
    1046          g = outer()
    1047          next(g)
    1048          v = None
    1049          g.send((1, 2, 3, 4))
    1050          self.assertEqual(v, (1, 2, 3, 4))
    1051  
    1052  class ESC[4;38;5;81mTestInterestingEdgeCases(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1053  
    1054      def assert_stop_iteration(self, iterator):
    1055          with self.assertRaises(StopIteration) as caught:
    1056              next(iterator)
    1057          self.assertIsNone(caught.exception.value)
    1058          self.assertIsNone(caught.exception.__context__)
    1059  
    1060      def assert_generator_raised_stop_iteration(self):
    1061          return self.assertRaisesRegex(RuntimeError, r"^generator raised StopIteration$")
    1062  
    1063      def assert_generator_ignored_generator_exit(self):
    1064          return self.assertRaisesRegex(RuntimeError, r"^generator ignored GeneratorExit$")
    1065  
    1066      def test_close_and_throw_work(self):
    1067  
    1068          yielded_first = object()
    1069          yielded_second = object()
    1070          returned = object()
    1071  
    1072          def inner():
    1073              yield yielded_first
    1074              yield yielded_second
    1075              return returned
    1076  
    1077          def outer():
    1078              return (yield from inner())
    1079  
    1080          with self.subTest("close"):
    1081              g = outer()
    1082              self.assertIs(next(g), yielded_first)
    1083              g.close()
    1084              self.assert_stop_iteration(g)
    1085  
    1086          with self.subTest("throw GeneratorExit"):
    1087              g = outer()
    1088              self.assertIs(next(g), yielded_first)
    1089              thrown = GeneratorExit()
    1090              with self.assertRaises(GeneratorExit) as caught:
    1091                  g.throw(thrown)
    1092              self.assertIs(caught.exception, thrown)
    1093              self.assertIsNone(caught.exception.__context__)
    1094              self.assert_stop_iteration(g)
    1095  
    1096          with self.subTest("throw StopIteration"):
    1097              g = outer()
    1098              self.assertIs(next(g), yielded_first)
    1099              thrown = StopIteration()
    1100              # PEP 479:
    1101              with self.assert_generator_raised_stop_iteration() as caught:
    1102                  g.throw(thrown)
    1103              self.assertIs(caught.exception.__context__, thrown)
    1104              self.assertIsNone(caught.exception.__context__.__context__)
    1105              self.assert_stop_iteration(g)
    1106  
    1107          with self.subTest("throw BaseException"):
    1108              g = outer()
    1109              self.assertIs(next(g), yielded_first)
    1110              thrown = BaseException()
    1111              with self.assertRaises(BaseException) as caught:
    1112                  g.throw(thrown)
    1113              self.assertIs(caught.exception, thrown)
    1114              self.assertIsNone(caught.exception.__context__)
    1115              self.assert_stop_iteration(g)
    1116  
    1117          with self.subTest("throw Exception"):
    1118              g = outer()
    1119              self.assertIs(next(g), yielded_first)
    1120              thrown = Exception()
    1121              with self.assertRaises(Exception) as caught:
    1122                  g.throw(thrown)
    1123              self.assertIs(caught.exception, thrown)
    1124              self.assertIsNone(caught.exception.__context__)
    1125              self.assert_stop_iteration(g)
    1126  
    1127      def test_close_and_throw_raise_generator_exit(self):
    1128  
    1129          yielded_first = object()
    1130          yielded_second = object()
    1131          returned = object()
    1132  
    1133          def inner():
    1134              try:
    1135                  yield yielded_first
    1136                  yield yielded_second
    1137                  return returned
    1138              finally:
    1139                  raise raised
    1140  
    1141          def outer():
    1142              return (yield from inner())
    1143  
    1144          with self.subTest("close"):
    1145              g = outer()
    1146              self.assertIs(next(g), yielded_first)
    1147              raised = GeneratorExit()
    1148              # GeneratorExit is suppressed. This is consistent with PEP 342:
    1149              # https://peps.python.org/pep-0342/#new-generator-method-close
    1150              g.close()
    1151              self.assert_stop_iteration(g)
    1152  
    1153          with self.subTest("throw GeneratorExit"):
    1154              g = outer()
    1155              self.assertIs(next(g), yielded_first)
    1156              raised = GeneratorExit()
    1157              thrown = GeneratorExit()
    1158              with self.assertRaises(GeneratorExit) as caught:
    1159                  g.throw(thrown)
    1160              # The raised GeneratorExit is suppressed, but the thrown one
    1161              # propagates. This is consistent with PEP 380:
    1162              # https://peps.python.org/pep-0380/#proposal
    1163              self.assertIs(caught.exception, thrown)
    1164              self.assertIsNone(caught.exception.__context__)
    1165              self.assert_stop_iteration(g)
    1166  
    1167          with self.subTest("throw StopIteration"):
    1168              g = outer()
    1169              self.assertIs(next(g), yielded_first)
    1170              raised = GeneratorExit()
    1171              thrown = StopIteration()
    1172              with self.assertRaises(GeneratorExit) as caught:
    1173                  g.throw(thrown)
    1174              self.assertIs(caught.exception, raised)
    1175              self.assertIs(caught.exception.__context__, thrown)
    1176              self.assertIsNone(caught.exception.__context__.__context__)
    1177              self.assert_stop_iteration(g)
    1178  
    1179          with self.subTest("throw BaseException"):
    1180              g = outer()
    1181              self.assertIs(next(g), yielded_first)
    1182              raised = GeneratorExit()
    1183              thrown = BaseException()
    1184              with self.assertRaises(GeneratorExit) as caught:
    1185                  g.throw(thrown)
    1186              self.assertIs(caught.exception, raised)
    1187              self.assertIs(caught.exception.__context__, thrown)
    1188              self.assertIsNone(caught.exception.__context__.__context__)
    1189              self.assert_stop_iteration(g)
    1190  
    1191          with self.subTest("throw Exception"):
    1192              g = outer()
    1193              self.assertIs(next(g), yielded_first)
    1194              raised = GeneratorExit()
    1195              thrown = Exception()
    1196              with self.assertRaises(GeneratorExit) as caught:
    1197                  g.throw(thrown)
    1198              self.assertIs(caught.exception, raised)
    1199              self.assertIs(caught.exception.__context__, thrown)
    1200              self.assertIsNone(caught.exception.__context__.__context__)
    1201              self.assert_stop_iteration(g)
    1202  
    1203      def test_close_and_throw_raise_stop_iteration(self):
    1204  
    1205          yielded_first = object()
    1206          yielded_second = object()
    1207          returned = object()
    1208  
    1209          def inner():
    1210              try:
    1211                  yield yielded_first
    1212                  yield yielded_second
    1213                  return returned
    1214              finally:
    1215                  raise raised
    1216  
    1217          def outer():
    1218              return (yield from inner())
    1219  
    1220          with self.subTest("close"):
    1221              g = outer()
    1222              self.assertIs(next(g), yielded_first)
    1223              raised = StopIteration()
    1224              # PEP 479:
    1225              with self.assert_generator_raised_stop_iteration() as caught:
    1226                  g.close()
    1227              self.assertIs(caught.exception.__context__, raised)
    1228              self.assertIsInstance(caught.exception.__context__.__context__, GeneratorExit)
    1229              self.assertIsNone(caught.exception.__context__.__context__.__context__)
    1230              self.assert_stop_iteration(g)
    1231  
    1232          with self.subTest("throw GeneratorExit"):
    1233              g = outer()
    1234              self.assertIs(next(g), yielded_first)
    1235              raised = StopIteration()
    1236              thrown = GeneratorExit()
    1237              # PEP 479:
    1238              with self.assert_generator_raised_stop_iteration() as caught:
    1239                  g.throw(thrown)
    1240              self.assertIs(caught.exception.__context__, raised)
    1241              # This isn't the same GeneratorExit as thrown! It's the one created
    1242              # by calling inner.close():
    1243              self.assertIsInstance(caught.exception.__context__.__context__, GeneratorExit)
    1244              self.assertIsNone(caught.exception.__context__.__context__.__context__)
    1245              self.assert_stop_iteration(g)
    1246  
    1247          with self.subTest("throw StopIteration"):
    1248              g = outer()
    1249              self.assertIs(next(g), yielded_first)
    1250              raised = StopIteration()
    1251              thrown = StopIteration()
    1252              # PEP 479:
    1253              with self.assert_generator_raised_stop_iteration() as caught:
    1254                  g.throw(thrown)
    1255              self.assertIs(caught.exception.__context__, raised)
    1256              self.assertIs(caught.exception.__context__.__context__, thrown)
    1257              self.assertIsNone(caught.exception.__context__.__context__.__context__)
    1258              self.assert_stop_iteration(g)
    1259  
    1260          with self.subTest("throw BaseException"):
    1261              g = outer()
    1262              self.assertIs(next(g), yielded_first)
    1263              raised = StopIteration()
    1264              thrown = BaseException()
    1265              # PEP 479:
    1266              with self.assert_generator_raised_stop_iteration() as caught:
    1267                  g.throw(thrown)
    1268              self.assertIs(caught.exception.__context__, raised)
    1269              self.assertIs(caught.exception.__context__.__context__, thrown)
    1270              self.assertIsNone(caught.exception.__context__.__context__.__context__)
    1271              self.assert_stop_iteration(g)
    1272  
    1273          with self.subTest("throw Exception"):
    1274              g = outer()
    1275              self.assertIs(next(g), yielded_first)
    1276              raised = StopIteration()
    1277              thrown = Exception()
    1278              # PEP 479:
    1279              with self.assert_generator_raised_stop_iteration() as caught:
    1280                  g.throw(thrown)
    1281              self.assertIs(caught.exception.__context__, raised)
    1282              self.assertIs(caught.exception.__context__.__context__, thrown)
    1283              self.assertIsNone(caught.exception.__context__.__context__.__context__)
    1284              self.assert_stop_iteration(g)
    1285  
    1286      def test_close_and_throw_raise_base_exception(self):
    1287  
    1288          yielded_first = object()
    1289          yielded_second = object()
    1290          returned = object()
    1291  
    1292          def inner():
    1293              try:
    1294                  yield yielded_first
    1295                  yield yielded_second
    1296                  return returned
    1297              finally:
    1298                  raise raised
    1299  
    1300          def outer():
    1301              return (yield from inner())
    1302  
    1303          with self.subTest("close"):
    1304              g = outer()
    1305              self.assertIs(next(g), yielded_first)
    1306              raised = BaseException()
    1307              with self.assertRaises(BaseException) as caught:
    1308                  g.close()
    1309              self.assertIs(caught.exception, raised)
    1310              self.assertIsInstance(caught.exception.__context__, GeneratorExit)
    1311              self.assertIsNone(caught.exception.__context__.__context__)
    1312              self.assert_stop_iteration(g)
    1313  
    1314          with self.subTest("throw GeneratorExit"):
    1315              g = outer()
    1316              self.assertIs(next(g), yielded_first)
    1317              raised = BaseException()
    1318              thrown = GeneratorExit()
    1319              with self.assertRaises(BaseException) as caught:
    1320                  g.throw(thrown)
    1321              self.assertIs(caught.exception, raised)
    1322              # This isn't the same GeneratorExit as thrown! It's the one created
    1323              # by calling inner.close():
    1324              self.assertIsInstance(caught.exception.__context__, GeneratorExit)
    1325              self.assertIsNone(caught.exception.__context__.__context__)
    1326              self.assert_stop_iteration(g)
    1327  
    1328          with self.subTest("throw StopIteration"):
    1329              g = outer()
    1330              self.assertIs(next(g), yielded_first)
    1331              raised = BaseException()
    1332              thrown = StopIteration()
    1333              with self.assertRaises(BaseException) as caught:
    1334                  g.throw(thrown)
    1335              self.assertIs(caught.exception, raised)
    1336              self.assertIs(caught.exception.__context__, thrown)
    1337              self.assertIsNone(caught.exception.__context__.__context__)
    1338              self.assert_stop_iteration(g)
    1339  
    1340          with self.subTest("throw BaseException"):
    1341              g = outer()
    1342              self.assertIs(next(g), yielded_first)
    1343              raised = BaseException()
    1344              thrown = BaseException()
    1345              with self.assertRaises(BaseException) as caught:
    1346                  g.throw(thrown)
    1347              self.assertIs(caught.exception, raised)
    1348              self.assertIs(caught.exception.__context__, thrown)
    1349              self.assertIsNone(caught.exception.__context__.__context__)
    1350              self.assert_stop_iteration(g)
    1351  
    1352          with self.subTest("throw Exception"):
    1353              g = outer()
    1354              self.assertIs(next(g), yielded_first)
    1355              raised = BaseException()
    1356              thrown = Exception()
    1357              with self.assertRaises(BaseException) as caught:
    1358                  g.throw(thrown)
    1359              self.assertIs(caught.exception, raised)
    1360              self.assertIs(caught.exception.__context__, thrown)
    1361              self.assertIsNone(caught.exception.__context__.__context__)
    1362              self.assert_stop_iteration(g)
    1363  
    1364      def test_close_and_throw_raise_exception(self):
    1365  
    1366          yielded_first = object()
    1367          yielded_second = object()
    1368          returned = object()
    1369  
    1370          def inner():
    1371              try:
    1372                  yield yielded_first
    1373                  yield yielded_second
    1374                  return returned
    1375              finally:
    1376                  raise raised
    1377  
    1378          def outer():
    1379              return (yield from inner())
    1380  
    1381          with self.subTest("close"):
    1382              g = outer()
    1383              self.assertIs(next(g), yielded_first)
    1384              raised = Exception()
    1385              with self.assertRaises(Exception) as caught:
    1386                  g.close()
    1387              self.assertIs(caught.exception, raised)
    1388              self.assertIsInstance(caught.exception.__context__, GeneratorExit)
    1389              self.assertIsNone(caught.exception.__context__.__context__)
    1390              self.assert_stop_iteration(g)
    1391  
    1392          with self.subTest("throw GeneratorExit"):
    1393              g = outer()
    1394              self.assertIs(next(g), yielded_first)
    1395              raised = Exception()
    1396              thrown = GeneratorExit()
    1397              with self.assertRaises(Exception) as caught:
    1398                  g.throw(thrown)
    1399              self.assertIs(caught.exception, raised)
    1400              # This isn't the same GeneratorExit as thrown! It's the one created
    1401              # by calling inner.close():
    1402              self.assertIsInstance(caught.exception.__context__, GeneratorExit)
    1403              self.assertIsNone(caught.exception.__context__.__context__)
    1404              self.assert_stop_iteration(g)
    1405  
    1406          with self.subTest("throw StopIteration"):
    1407              g = outer()
    1408              self.assertIs(next(g), yielded_first)
    1409              raised = Exception()
    1410              thrown = StopIteration()
    1411              with self.assertRaises(Exception) as caught:
    1412                  g.throw(thrown)
    1413              self.assertIs(caught.exception, raised)
    1414              self.assertIs(caught.exception.__context__, thrown)
    1415              self.assertIsNone(caught.exception.__context__.__context__)
    1416              self.assert_stop_iteration(g)
    1417  
    1418          with self.subTest("throw BaseException"):
    1419              g = outer()
    1420              self.assertIs(next(g), yielded_first)
    1421              raised = Exception()
    1422              thrown = BaseException()
    1423              with self.assertRaises(Exception) as caught:
    1424                  g.throw(thrown)
    1425              self.assertIs(caught.exception, raised)
    1426              self.assertIs(caught.exception.__context__, thrown)
    1427              self.assertIsNone(caught.exception.__context__.__context__)
    1428              self.assert_stop_iteration(g)
    1429  
    1430          with self.subTest("throw Exception"):
    1431              g = outer()
    1432              self.assertIs(next(g), yielded_first)
    1433              raised = Exception()
    1434              thrown = Exception()
    1435              with self.assertRaises(Exception) as caught:
    1436                  g.throw(thrown)
    1437              self.assertIs(caught.exception, raised)
    1438              self.assertIs(caught.exception.__context__, thrown)
    1439              self.assertIsNone(caught.exception.__context__.__context__)
    1440              self.assert_stop_iteration(g)
    1441  
    1442      def test_close_and_throw_yield(self):
    1443  
    1444          yielded_first = object()
    1445          yielded_second = object()
    1446          returned = object()
    1447  
    1448          def inner():
    1449              try:
    1450                  yield yielded_first
    1451              finally:
    1452                  yield yielded_second
    1453              return returned
    1454  
    1455          def outer():
    1456              return (yield from inner())
    1457  
    1458          with self.subTest("close"):
    1459              g = outer()
    1460              self.assertIs(next(g), yielded_first)
    1461              # No chaining happens. This is consistent with PEP 342:
    1462              # https://peps.python.org/pep-0342/#new-generator-method-close
    1463              with self.assert_generator_ignored_generator_exit() as caught:
    1464                  g.close()
    1465              self.assertIsNone(caught.exception.__context__)
    1466              self.assert_stop_iteration(g)
    1467  
    1468          with self.subTest("throw GeneratorExit"):
    1469              g = outer()
    1470              self.assertIs(next(g), yielded_first)
    1471              thrown = GeneratorExit()
    1472              # No chaining happens. This is consistent with PEP 342:
    1473              # https://peps.python.org/pep-0342/#new-generator-method-close
    1474              with self.assert_generator_ignored_generator_exit() as caught:
    1475                  g.throw(thrown)
    1476              self.assertIsNone(caught.exception.__context__)
    1477              self.assert_stop_iteration(g)
    1478  
    1479          with self.subTest("throw StopIteration"):
    1480              g = outer()
    1481              self.assertIs(next(g), yielded_first)
    1482              thrown = StopIteration()
    1483              self.assertEqual(g.throw(thrown), yielded_second)
    1484              # PEP 479:
    1485              with self.assert_generator_raised_stop_iteration() as caught:
    1486                  next(g)
    1487              self.assertIs(caught.exception.__context__, thrown)
    1488              self.assertIsNone(caught.exception.__context__.__context__)
    1489              self.assert_stop_iteration(g)
    1490  
    1491          with self.subTest("throw BaseException"):
    1492              g = outer()
    1493              self.assertIs(next(g), yielded_first)
    1494              thrown = BaseException()
    1495              self.assertEqual(g.throw(thrown), yielded_second)
    1496              with self.assertRaises(BaseException) as caught:
    1497                  next(g)
    1498              self.assertIs(caught.exception, thrown)
    1499              self.assertIsNone(caught.exception.__context__)
    1500              self.assert_stop_iteration(g)
    1501  
    1502          with self.subTest("throw Exception"):
    1503              g = outer()
    1504              self.assertIs(next(g), yielded_first)
    1505              thrown = Exception()
    1506              self.assertEqual(g.throw(thrown), yielded_second)
    1507              with self.assertRaises(Exception) as caught:
    1508                  next(g)
    1509              self.assertIs(caught.exception, thrown)
    1510              self.assertIsNone(caught.exception.__context__)
    1511              self.assert_stop_iteration(g)
    1512  
    1513      def test_close_and_throw_return(self):
    1514  
    1515          yielded_first = object()
    1516          yielded_second = object()
    1517          returned = object()
    1518  
    1519          def inner():
    1520              try:
    1521                  yield yielded_first
    1522                  yield yielded_second
    1523              finally:
    1524                  return returned
    1525  
    1526          def outer():
    1527              return (yield from inner())
    1528  
    1529          with self.subTest("close"):
    1530              g = outer()
    1531              self.assertIs(next(g), yielded_first)
    1532              # StopIteration is suppressed. This is consistent with PEP 342:
    1533              # https://peps.python.org/pep-0342/#new-generator-method-close
    1534              g.close()
    1535              self.assert_stop_iteration(g)
    1536  
    1537          with self.subTest("throw GeneratorExit"):
    1538              g = outer()
    1539              self.assertIs(next(g), yielded_first)
    1540              thrown = GeneratorExit()
    1541              # StopIteration is suppressed. This is consistent with PEP 342:
    1542              # https://peps.python.org/pep-0342/#new-generator-method-close
    1543              with self.assertRaises(GeneratorExit) as caught:
    1544                  g.throw(thrown)
    1545              self.assertIs(caught.exception, thrown)
    1546              self.assertIsNone(caught.exception.__context__)
    1547              self.assert_stop_iteration(g)
    1548  
    1549          with self.subTest("throw StopIteration"):
    1550              g = outer()
    1551              self.assertIs(next(g), yielded_first)
    1552              thrown = StopIteration()
    1553              with self.assertRaises(StopIteration) as caught:
    1554                  g.throw(thrown)
    1555              self.assertIs(caught.exception.value, returned)
    1556              self.assertIsNone(caught.exception.__context__)
    1557              self.assert_stop_iteration(g)
    1558  
    1559          with self.subTest("throw BaseException"):
    1560              g = outer()
    1561              self.assertIs(next(g), yielded_first)
    1562              thrown = BaseException()
    1563              with self.assertRaises(StopIteration) as caught:
    1564                  g.throw(thrown)
    1565              self.assertIs(caught.exception.value, returned)
    1566              self.assertIsNone(caught.exception.__context__)
    1567              self.assert_stop_iteration(g)
    1568  
    1569          with self.subTest("throw Exception"):
    1570              g = outer()
    1571              self.assertIs(next(g), yielded_first)
    1572              thrown = Exception()
    1573              with self.assertRaises(StopIteration) as caught:
    1574                  g.throw(thrown)
    1575              self.assertIs(caught.exception.value, returned)
    1576              self.assertIsNone(caught.exception.__context__)
    1577              self.assert_stop_iteration(g)
    1578  
    1579  
    1580  if __name__ == '__main__':
    1581      unittest.main()