Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a1c48a7

Browse filesBrowse files
miss-islingtongraingertkumaraditya303
authored
[3.13] gh-128588: gh-128550: remove eager tasks optimization that missed and introduced incorrect cancellations (GH-129063) (#129089)
gh-128588: gh-128550: remove eager tasks optimization that missed and introduced incorrect cancellations (GH-129063) (cherry picked from commit ed6934e) Co-authored-by: Thomas Grainger <tagrain@gmail.com> Co-authored-by: Kumar Aditya <kumaraditya@python.org>
1 parent 22e9faf commit a1c48a7
Copy full SHA for a1c48a7

File tree

Expand file treeCollapse file tree

3 files changed

+56
-7
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+56
-7
lines changed

‎Lib/asyncio/taskgroups.py

Copy file name to clipboardExpand all lines: Lib/asyncio/taskgroups.py
+5-7Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,12 @@ def create_task(self, coro, *, name=None, context=None):
197197
else:
198198
task = self._loop.create_task(coro, name=name, context=context)
199199

200-
# optimization: Immediately call the done callback if the task is
200+
# Always schedule the done callback even if the task is
201201
# already done (e.g. if the coro was able to complete eagerly),
202-
# and skip scheduling a done callback
203-
if task.done():
204-
self._on_task_done(task)
205-
else:
206-
self._tasks.add(task)
207-
task.add_done_callback(self._on_task_done)
202+
# otherwise if the task completes with an exception then it will cancel
203+
# the current task too early. gh-128550, gh-128588
204+
self._tasks.add(task)
205+
task.add_done_callback(self._on_task_done)
208206
try:
209207
return task
210208
finally:

‎Lib/test/test_asyncio/test_taskgroups.py

Copy file name to clipboardExpand all lines: Lib/test/test_asyncio/test_taskgroups.py
+50Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,56 @@ class MyKeyboardInterrupt(KeyboardInterrupt):
10321032
self.assertListEqual(gc.get_referrers(exc), [])
10331033

10341034

1035+
async def test_cancels_task_if_created_during_creation(self):
1036+
# regression test for gh-128550
1037+
ran = False
1038+
class MyError(Exception):
1039+
pass
1040+
1041+
exc = None
1042+
try:
1043+
async with asyncio.TaskGroup() as tg:
1044+
async def third_task():
1045+
raise MyError("third task failed")
1046+
1047+
async def second_task():
1048+
nonlocal ran
1049+
tg.create_task(third_task())
1050+
with self.assertRaises(asyncio.CancelledError):
1051+
await asyncio.sleep(0) # eager tasks cancel here
1052+
await asyncio.sleep(0) # lazy tasks cancel here
1053+
ran = True
1054+
1055+
tg.create_task(second_task())
1056+
except* MyError as excs:
1057+
exc = excs.exceptions[0]
1058+
1059+
self.assertTrue(ran)
1060+
self.assertIsInstance(exc, MyError)
1061+
1062+
1063+
async def test_cancellation_does_not_leak_out_of_tg(self):
1064+
class MyError(Exception):
1065+
pass
1066+
1067+
async def throw_error():
1068+
raise MyError
1069+
1070+
try:
1071+
async with asyncio.TaskGroup() as tg:
1072+
tg.create_task(throw_error())
1073+
except* MyError:
1074+
pass
1075+
else:
1076+
self.fail("should have raised one MyError in group")
1077+
1078+
# if this test fails this current task will be cancelled
1079+
# outside the task group and inside unittest internals
1080+
# we yield to the event loop with sleep(0) so that
1081+
# cancellation happens here and error is more understandable
1082+
await asyncio.sleep(0)
1083+
1084+
10351085
class TestTaskGroup(BaseTestTaskGroup, unittest.IsolatedAsyncioTestCase):
10361086
loop_factory = asyncio.EventLoop
10371087

+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Removed an incorrect optimization relating to eager tasks in :class:`asyncio.TaskGroup` that resulted in cancellations being missed.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.