GH-95097: fix asyncio.run for tasks without uncancel method (#95211)

Co-authored-by: Thomas Grainger <tagrain@gmail.com>
This commit is contained in:
Kumar Aditya 2022-07-28 21:17:54 +05:30 committed by GitHub
parent bceb197947
commit 54f48844d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 6 deletions

View File

@ -118,10 +118,11 @@ class Runner:
events.set_event_loop(self._loop) events.set_event_loop(self._loop)
return self._loop.run_until_complete(task) return self._loop.run_until_complete(task)
except exceptions.CancelledError: except exceptions.CancelledError:
if self._interrupt_count > 0 and task.uncancel() == 0: if self._interrupt_count > 0:
raise KeyboardInterrupt() uncancel = getattr(task, "uncancel", None)
else: if uncancel is not None and uncancel() == 0:
raise # CancelledError raise KeyboardInterrupt()
raise # CancelledError
finally: finally:
if (sigint_handler is not None if (sigint_handler is not None
and signal.getsignal(signal.SIGINT) is sigint_handler and signal.getsignal(signal.SIGINT) is sigint_handler

View File

@ -5,10 +5,9 @@ import re
import signal import signal
import threading import threading
import unittest import unittest
from test.test_asyncio import utils as test_utils
from unittest import mock from unittest import mock
from unittest.mock import patch from unittest.mock import patch
from test.test_asyncio import utils as test_utils
def tearDownModule(): def tearDownModule():
@ -210,6 +209,54 @@ class RunTests(BaseTest):
asyncio.run(main()) asyncio.run(main())
self.assertTrue(policy.set_event_loop.called) self.assertTrue(policy.set_event_loop.called)
def test_asyncio_run_without_uncancel(self):
# See https://github.com/python/cpython/issues/95097
class Task:
def __init__(self, loop, coro, **kwargs):
self._task = asyncio.Task(coro, loop=loop, **kwargs)
def cancel(self, *args, **kwargs):
return self._task.cancel(*args, **kwargs)
def add_done_callback(self, *args, **kwargs):
return self._task.add_done_callback(*args, **kwargs)
def remove_done_callback(self, *args, **kwargs):
return self._task.remove_done_callback(*args, **kwargs)
@property
def _asyncio_future_blocking(self):
return self._task._asyncio_future_blocking
def result(self, *args, **kwargs):
return self._task.result(*args, **kwargs)
def done(self, *args, **kwargs):
return self._task.done(*args, **kwargs)
def cancelled(self, *args, **kwargs):
return self._task.cancelled(*args, **kwargs)
def exception(self, *args, **kwargs):
return self._task.exception(*args, **kwargs)
def get_loop(self, *args, **kwargs):
return self._task.get_loop(*args, **kwargs)
async def main():
interrupt_self()
await asyncio.Event().wait()
def new_event_loop():
loop = self.new_loop()
loop.set_task_factory(Task)
return loop
asyncio.set_event_loop_policy(TestPolicy(new_event_loop))
with self.assertRaises(asyncio.CancelledError):
asyncio.run(main())
class RunnerTests(BaseTest): class RunnerTests(BaseTest):

View File

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