@@ -69,6 +69,20 @@ async def run():
6969
7070 self .assertEqual (self .run_coro (run ()), 'my message' )
7171
72+ def test_awaited_by_during_eager_step (self ):
73+ async def run ():
74+ parent = asyncio .current_task ()
75+
76+ async def coro ():
77+ self .assertEqual (asyncio .current_task ()._asyncio_awaited_by ,
78+ frozenset ({parent }))
79+
80+ t = self .loop .create_task (coro ())
81+ self .assertFalse (t ._asyncio_awaited_by )
82+ await t
83+
84+ self .run_coro (run ())
85+
7286 def test_eager_completion (self ):
7387
7488 async def coro ():
@@ -292,11 +306,29 @@ def setUp(self):
292306 self ._current_task = asyncio .current_task
293307 asyncio .current_task = asyncio .tasks .current_task = asyncio .tasks ._py_current_task
294308 asyncio .all_tasks = asyncio .tasks .all_tasks = asyncio .tasks ._py_all_tasks
309+
310+ futures = asyncio .futures
311+
312+ self ._future_add_to_awaited_by = asyncio .future_add_to_awaited_by
313+ futures .future_add_to_awaited_by = futures ._py_future_add_to_awaited_by
314+ asyncio .future_add_to_awaited_by = futures .future_add_to_awaited_by
315+
316+ self ._future_discard_from_awaited_by = asyncio .future_discard_from_awaited_by
317+ futures .future_discard_from_awaited_by = futures ._py_future_discard_from_awaited_by
318+ asyncio .future_discard_from_awaited_by = futures .future_discard_from_awaited_by
295319 return super ().setUp ()
296320
297321 def tearDown (self ):
298322 asyncio .current_task = asyncio .tasks .current_task = self ._current_task
299323 asyncio .all_tasks = asyncio .tasks .all_tasks = self ._all_tasks
324+
325+ futures = asyncio .futures
326+
327+ futures .future_discard_from_awaited_by = self ._future_discard_from_awaited_by
328+ asyncio .future_discard_from_awaited_by = self ._future_discard_from_awaited_by
329+
330+ futures .future_add_to_awaited_by = self ._future_add_to_awaited_by
331+ asyncio .future_add_to_awaited_by = self ._future_add_to_awaited_by
300332 return super ().tearDown ()
301333
302334
@@ -318,6 +350,49 @@ def tearDown(self):
318350 asyncio .all_tasks = asyncio .tasks .all_tasks = self ._all_tasks
319351 return super ().tearDown ()
320352
353+ def test_eager_step_with_non_native_parent (self ):
354+ async def child ():
355+ self .assertFalse (asyncio .current_task ()._asyncio_awaited_by )
356+
357+ async def run ():
358+ parent = tasks ._c_swap_current_task (self .loop , object ())
359+ try :
360+ task = self .loop .create_task (child ())
361+ finally :
362+ tasks ._c_swap_current_task (self .loop , parent )
363+ await task
364+
365+ self .run_coro (run ())
366+
367+ def test_eager_step_preserves_exception (self ):
368+ # gh-157299: check that the exception is saved and cleared before
369+ # discarding the awaited-by edge, and restored afterwards.
370+ # A default task can still pass with the wrong order since its hash
371+ # is in C. Overriding __hash__ makes cleanup call back into Python.
372+ # If the exception is still pending, this raises SystemError.
373+ class ParentTask (self .Task ):
374+ def __hash__ (self ):
375+ return super ().__hash__ ()
376+
377+ child_tasks = []
378+ exc = SystemExit ("eager step" )
379+
380+ async def child ():
381+ task = asyncio .current_task ()
382+ child_tasks .append (task )
383+ asyncio .future_add_to_awaited_by (task , self .loop .create_future ())
384+ raise exc
385+
386+ async def parent ():
387+ with self .assertRaises (SystemExit ) as cm :
388+ self .loop .create_task (child ())
389+ task , = child_tasks
390+ self .assertIs (cm .exception , exc )
391+ self .assertIs (task .exception (), exc )
392+ self .assertNotIn (asyncio .current_task (), task ._asyncio_awaited_by )
393+
394+ self .loop .run_until_complete (ParentTask (parent (), loop = self .loop ))
395+
321396 def test_issue105987 (self ):
322397 code = """if 1:
323398 from _asyncio import _swap_current_task, _set_running_loop
0 commit comments