|
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 |
|
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 |
|
|