Skip to content

Commit efeda8b

Browse files
miss-islingtonkumaraditya303graingert
authored
GH-95097: fix asyncio.run for tasks without uncancel method (GH-95211) (GH-95387)
(cherry picked from commit 54f4884) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: Thomas Grainger <tagrain@gmail.com>
1 parent c26470f commit efeda8b

File tree

3 files changed

+55
-6
lines changed

3 files changed

+55
-6
lines changed

Lib/asyncio/runners.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,11 @@ def run(self, coro, *, context=None):
119119
events.set_event_loop(self._loop)
120120
return self._loop.run_until_complete(task)
121121
except exceptions.CancelledError:
122-
if self._interrupt_count > 0 and task.uncancel() == 0:
123-
raise KeyboardInterrupt()
124-
else:
125-
raise # CancelledError
122+
if self._interrupt_count > 0:
123+
uncancel = getattr(task, "uncancel", None)
124+
if uncancel is not None and uncancel() == 0:
125+
raise KeyboardInterrupt()
126+
raise # CancelledError
126127
finally:
127128
if (sigint_handler is not None
128129
and signal.getsignal(signal.SIGINT) is sigint_handler

Lib/test/test_asyncio/test_runners.py

+49-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
import signal
77
import threading
88
import unittest
9-
9+
from test.test_asyncio import utils as test_utils
1010
from unittest import mock
1111
from unittest.mock import patch
12-
from test.test_asyncio import utils as test_utils
1312

1413

1514
def tearDownModule():
@@ -211,6 +210,54 @@ async def main():
211210
asyncio.run(main())
212211
self.assertTrue(policy.set_event_loop.called)
213212

213+
def test_asyncio_run_without_uncancel(self):
214+
# See https://github.com/python/cpython/issues/95097
215+
class Task:
216+
def __init__(self, loop, coro, **kwargs):
217+
self._task = asyncio.Task(coro, loop=loop, **kwargs)
218+
219+
def cancel(self, *args, **kwargs):
220+
return self._task.cancel(*args, **kwargs)
221+
222+
def add_done_callback(self, *args, **kwargs):
223+
return self._task.add_done_callback(*args, **kwargs)
224+
225+
def remove_done_callback(self, *args, **kwargs):
226+
return self._task.remove_done_callback(*args, **kwargs)
227+
228+
@property
229+
def _asyncio_future_blocking(self):
230+
return self._task._asyncio_future_blocking
231+
232+
def result(self, *args, **kwargs):
233+
return self._task.result(*args, **kwargs)
234+
235+
def done(self, *args, **kwargs):
236+
return self._task.done(*args, **kwargs)
237+
238+
def cancelled(self, *args, **kwargs):
239+
return self._task.cancelled(*args, **kwargs)
240+
241+
def exception(self, *args, **kwargs):
242+
return self._task.exception(*args, **kwargs)
243+
244+
def get_loop(self, *args, **kwargs):
245+
return self._task.get_loop(*args, **kwargs)
246+
247+
248+
async def main():
249+
interrupt_self()
250+
await asyncio.Event().wait()
251+
252+
def new_event_loop():
253+
loop = self.new_loop()
254+
loop.set_task_factory(Task)
255+
return loop
256+
257+
asyncio.set_event_loop_policy(TestPolicy(new_event_loop))
258+
with self.assertRaises(asyncio.CancelledError):
259+
asyncio.run(main())
260+
214261

215262
class RunnerTests(BaseTest):
216263

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :func:`asyncio.run` for :class:`asyncio.Task` implementations without :meth:`~asyncio.Task.uncancel` method. Patch by Kumar Aditya.

0 commit comments

Comments
 (0)