mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Closes #22429, asyncio: Fix EventLoop.run_until_complete(), don't stop the
event loop if a BaseException is raised, because the event loop is already stopped.
This commit is contained in:
parent
4c85ec99f3
commit
f3e2e09213
2 changed files with 37 additions and 2 deletions
|
@ -102,6 +102,16 @@ def _raise_stop_error(*args):
|
||||||
raise _StopError
|
raise _StopError
|
||||||
|
|
||||||
|
|
||||||
|
def _run_until_complete_cb(fut):
|
||||||
|
exc = fut._exception
|
||||||
|
if (isinstance(exc, BaseException)
|
||||||
|
and not isinstance(exc, Exception)):
|
||||||
|
# Issue #22429: run_forever() already finished, no need to
|
||||||
|
# stop it.
|
||||||
|
return
|
||||||
|
_raise_stop_error()
|
||||||
|
|
||||||
|
|
||||||
class Server(events.AbstractServer):
|
class Server(events.AbstractServer):
|
||||||
|
|
||||||
def __init__(self, loop, sockets):
|
def __init__(self, loop, sockets):
|
||||||
|
@ -268,7 +278,7 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||||
# is no need to log the "destroy pending task" message
|
# is no need to log the "destroy pending task" message
|
||||||
future._log_destroy_pending = False
|
future._log_destroy_pending = False
|
||||||
|
|
||||||
future.add_done_callback(_raise_stop_error)
|
future.add_done_callback(_run_until_complete_cb)
|
||||||
try:
|
try:
|
||||||
self.run_forever()
|
self.run_forever()
|
||||||
except:
|
except:
|
||||||
|
@ -278,7 +288,7 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||||
# local task.
|
# local task.
|
||||||
future.exception()
|
future.exception()
|
||||||
raise
|
raise
|
||||||
future.remove_done_callback(_raise_stop_error)
|
future.remove_done_callback(_run_until_complete_cb)
|
||||||
if not future.done():
|
if not future.done():
|
||||||
raise RuntimeError('Event loop stopped before Future completed.')
|
raise RuntimeError('Event loop stopped before Future completed.')
|
||||||
|
|
||||||
|
|
|
@ -638,6 +638,31 @@ class BaseEventLoopTests(test_utils.TestCase):
|
||||||
|
|
||||||
self.assertFalse(self.loop.call_exception_handler.called)
|
self.assertFalse(self.loop.call_exception_handler.called)
|
||||||
|
|
||||||
|
def test_run_until_complete_baseexception(self):
|
||||||
|
# Python issue #22429: run_until_complete() must not schedule a pending
|
||||||
|
# call to stop() if the future raised a BaseException
|
||||||
|
@asyncio.coroutine
|
||||||
|
def raise_keyboard_interrupt():
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
|
self.loop._process_events = mock.Mock()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.loop.run_until_complete(raise_keyboard_interrupt())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def func():
|
||||||
|
self.loop.stop()
|
||||||
|
func.called = True
|
||||||
|
func.called = False
|
||||||
|
try:
|
||||||
|
self.loop.call_soon(func)
|
||||||
|
self.loop.run_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
self.assertTrue(func.called)
|
||||||
|
|
||||||
|
|
||||||
class MyProto(asyncio.Protocol):
|
class MyProto(asyncio.Protocol):
|
||||||
done = None
|
done = None
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue