Skip to content

Commit

Permalink
bpo-37028: asyncio REPL; activated via 'python -m asyncio'. (GH-13472)
Browse files Browse the repository at this point in the history
This makes it easy to play with asyncio APIs with simply
using async/await in the REPL.
  • Loading branch information
1st1 committed May 27, 2019
1 parent 71c52e3 commit 16cefb0
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
125 changes: 125 additions & 0 deletions Lib/asyncio/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import ast
import asyncio
import code
import concurrent.futures
import inspect
import sys
import threading
import types
import warnings

from . import futures


class AsyncIOInteractiveConsole(code.InteractiveConsole):

def __init__(self, locals, loop):
super().__init__(locals)
self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT

self.loop = loop

def runcode(self, code):
future = concurrent.futures.Future()

def callback():
global repl_future
global repl_future_interrupted

repl_future = None
repl_future_interrupted = False

func = types.FunctionType(code, self.locals)
try:
coro = func()
except SystemExit:
raise
except KeyboardInterrupt as ex:
repl_future_interrupted = True
future.set_exception(ex)
return
except BaseException as ex:
future.set_exception(ex)
return

if not inspect.iscoroutine(coro):
future.set_result(coro)
return

try:
repl_future = self.loop.create_task(coro)
futures._chain_future(repl_future, future)
except BaseException as exc:
future.set_exception(exc)

loop.call_soon_threadsafe(callback)

try:
return future.result()
except SystemExit:
raise
except BaseException:
if repl_future_interrupted:
self.write("\nKeyboardInterrupt\n")
else:
self.showtraceback()


class REPLThread(threading.Thread):

def run(self):
try:
banner = (
f'asyncio REPL {sys.version} on {sys.platform}\n'
f'Use "await" directly instead of "asyncio.run()".\n'
f'Type "help", "copyright", "credits" or "license" '
f'for more information.\n'
f'{getattr(sys, "ps1", ">>> ")}import asyncio'
)

console.interact(
banner=banner,
exitmsg='exiting asyncio REPL...')
finally:
warnings.filterwarnings(
'ignore',
message=r'^coroutine .* was never awaited$',
category=RuntimeWarning)

loop.call_soon_threadsafe(loop.stop)


if __name__ == '__main__':
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

repl_locals = {'asyncio': asyncio}
for key in {'__name__', '__package__',
'__loader__', '__spec__',
'__builtins__', '__file__'}:
repl_locals[key] = locals()[key]

console = AsyncIOInteractiveConsole(repl_locals, loop)

repl_future = None
repl_future_interrupted = False

try:
import readline # NoQA
except ImportError:
pass

repl_thread = REPLThread()
repl_thread.daemon = True
repl_thread.start()

while True:
try:
loop.run_forever()
except KeyboardInterrupt:
if repl_future and not repl_future.done():
repl_future.cancel()
repl_future_interrupted = True
continue
else:
break
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement asyncio REPL

0 comments on commit 16cefb0

Please sign in to comment.