Skip to content

Commit

Permalink
Closes python#22429, asyncio: Fix EventLoop.run_until_complete(), don…
Browse files Browse the repository at this point in the history
…'t stop the

event loop if a BaseException is raised, because the event loop is already
stopped.
  • Loading branch information
vstinner committed Dec 5, 2014
1 parent 4c85ec9 commit f3e2e09
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 2 deletions.
14 changes: 12 additions & 2 deletions Lib/asyncio/base_events.py
Expand Up @@ -102,6 +102,16 @@ def _raise_stop_error(*args):
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):

def __init__(self, loop, sockets):
Expand Down Expand Up @@ -268,7 +278,7 @@ def run_until_complete(self, future):
# is no need to log the "destroy pending task" message
future._log_destroy_pending = False

future.add_done_callback(_raise_stop_error)
future.add_done_callback(_run_until_complete_cb)
try:
self.run_forever()
except:
Expand All @@ -278,7 +288,7 @@ def run_until_complete(self, future):
# local task.
future.exception()
raise
future.remove_done_callback(_raise_stop_error)
future.remove_done_callback(_run_until_complete_cb)
if not future.done():
raise RuntimeError('Event loop stopped before Future completed.')

Expand Down
25 changes: 25 additions & 0 deletions Lib/test/test_asyncio/test_base_events.py
Expand Up @@ -638,6 +638,31 @@ def raise_keyboard_interrupt():

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):
done = None
Expand Down

0 comments on commit f3e2e09

Please sign in to comment.