@@ -1695,6 +1695,32 @@ async def run():
1695
1695
1696
1696
self .loop .run_until_complete (run ())
1697
1697
1698
+ def test_send_exhausted (self ):
1699
+ """
1700
+ Test the behaviour of an exhausted async generator
1701
+ """
1702
+
1703
+ async def run ():
1704
+ # test exhausted generator
1705
+ async def gen ():
1706
+ yield 1
1707
+
1708
+ g = gen ()
1709
+ [f async for f in g ]
1710
+
1711
+ # an exhausted generator behaves like this:
1712
+
1713
+ with self .assertRaises (StopAsyncIteration ):
1714
+ r = await g .asend (None )
1715
+ with self .assertRaises (StopAsyncIteration ):
1716
+ r = await g .__anext__ ()
1717
+
1718
+ # The following behaviour may be undefined:
1719
+ r = await g .athrow (EOFError )
1720
+ assert r is None
1721
+
1722
+ self .loop .run_until_complete (run ())
1723
+
1698
1724
1699
1725
class TestUnawaitedWarnings (unittest .TestCase ):
1700
1726
def test_asend (self ):
@@ -1728,6 +1754,97 @@ async def gen():
1728
1754
gc_collect ()
1729
1755
1730
1756
1757
+ class TestIssueGH74956 (unittest .TestCase ):
1758
+ # simultanous use of generator by different coroutines is not
1759
+ # allowed.
1760
+ # https://github.com/python/cpython/issues/74956
1761
+
1762
+ def setUp (self ):
1763
+ self .loop = asyncio .new_event_loop ()
1764
+ asyncio .set_event_loop (None )
1765
+
1766
+ def tearDown (self ):
1767
+ self .loop .close ()
1768
+ self .loop = None
1769
+ asyncio .set_event_loop_policy (None )
1770
+
1771
+ def test_simultaneous_asend (self ):
1772
+ """
1773
+ Verify that simultaneous use of generator by different coroutines is not
1774
+ permitted
1775
+ """
1776
+
1777
+ async def run ():
1778
+ async def consumer ():
1779
+ while True :
1780
+ await sleep (0 )
1781
+ yield
1782
+
1783
+ agenerator = consumer ()
1784
+ await agenerator .asend (None )
1785
+ fa = asyncio .create_task (agenerator .asend ("A" ))
1786
+ fb = asyncio .create_task (agenerator .asend ("B" ))
1787
+ await fa
1788
+ with self .assertRaises (RuntimeError ) as err :
1789
+ await fb
1790
+ assert "already running" in str (err .exception )
1791
+
1792
+ agenerator = consumer ()
1793
+ await agenerator .asend (None )
1794
+ fa = asyncio .create_task (agenerator .asend ("A" ))
1795
+ fb = asyncio .create_task (agenerator .athrow (EOFError ))
1796
+ await fa
1797
+ with self .assertRaises (RuntimeError ) as err :
1798
+ await fb
1799
+ assert "already running" in str (err .exception )
1800
+
1801
+ await agenerator .asend (None )
1802
+ fa = asyncio .create_task (agenerator .asend ("A" ))
1803
+ fb = asyncio .create_task (agenerator .aclose ())
1804
+ await fa
1805
+ with self .assertRaises (RuntimeError ) as err :
1806
+ await fb
1807
+ assert "already running" in str (err .exception )
1808
+
1809
+ self .loop .run_until_complete (run ())
1810
+
1811
+ def test_ag_running (self ):
1812
+ """
1813
+ Verify that as_running transitions correctly in
1814
+ an async generator
1815
+ """
1816
+ state = 0
1817
+
1818
+ async def agen ():
1819
+ nonlocal state
1820
+ state = 1
1821
+ await asyncio .sleep (0 )
1822
+ state = 2
1823
+ value = yield "foo"
1824
+ state = value
1825
+
1826
+ a = agen ()
1827
+ assert a .ag_running is False
1828
+ # start it running
1829
+ coro = a .asend (None )
1830
+ assert state == 0
1831
+ coro .send (None )
1832
+ assert state == 1
1833
+ assert a .ag_running is True
1834
+
1835
+ # wake it from sleep and have it yield
1836
+ with self .assertRaises (StopIteration ) as v :
1837
+ coro .send (None )
1838
+ assert v .exception .value == "foo"
1839
+ assert state == 2
1840
+ assert a .ag_running is False
1841
+
1842
+ # finish it
1843
+ coro = a .asend ("bar" )
1844
+ self .assertRaises (StopAsyncIteration , coro .send , None )
1845
+ assert a .ag_running is False
1846
+ assert state == "bar"
1847
+
1731
1848
1732
1849
if __name__ == "__main__" :
1733
1850
unittest .main ()
0 commit comments