diff options
author | Yury Selivanov <yury@magic.io> | 2019-05-27 11:42:29 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-27 11:42:29 (GMT) |
commit | 16cefb0bc7b05c08caf08525398ff178c35dece4 (patch) | |
tree | fa4cebab889b622d48d026f27d102758a2905307 /Lib/asyncio/__main__.py | |
parent | 71c52e3048dd07567f0c690eab4e5d57be66f534 (diff) | |
download | cpython-16cefb0bc7b05c08caf08525398ff178c35dece4.zip cpython-16cefb0bc7b05c08caf08525398ff178c35dece4.tar.gz cpython-16cefb0bc7b05c08caf08525398ff178c35dece4.tar.bz2 |
bpo-37028: asyncio REPL; activated via 'python -m asyncio'. (GH-13472)
This makes it easy to play with asyncio APIs with simply
using async/await in the REPL.
Diffstat (limited to 'Lib/asyncio/__main__.py')
-rw-r--r-- | Lib/asyncio/__main__.py | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py new file mode 100644 index 0000000..18bb87a --- /dev/null +++ b/Lib/asyncio/__main__.py @@ -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 |