Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-37028: Implement asyncio REPL (activated via 'python -m asyncio') #13472

Merged
merged 6 commits into from May 27, 2019
@@ -0,0 +1,124 @@
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)
This conversation was marked as resolved by asvetlov

This comment has been minimized.

Copy link
@asvetlov

asvetlov May 23, 2019

Contributor

Isn't asyncio.run_coroutine_threadsafe exists for replacing almost the whole callback() code?

This comment has been minimized.

Copy link
@1st1

1st1 May 23, 2019

Author Member

Almost, but not all. The idea here is that if the user is executing something() as opposed to await otherthing(), the former must execute in the main thread too.

This comment has been minimized.

Copy link
@1st1

1st1 May 23, 2019

Author Member

That's why I needed to copy/paste some of the run_coroutine_threadsafe implementation to make it work for our use case. Since it's all in asyncio, I see no problem in that.

This comment has been minimized.

Copy link
@asvetlov

asvetlov May 23, 2019

Contributor

Ok, no problem.

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\n'
f'Use "await" directly instead of "asyncio.run()".\n'
f'Type "help", "copyright", "credits" or "license" '
f'for more information.\n\n'
f'{getattr(sys, "ps1", ">>> ")}import asyncio\n'
This conversation was marked as resolved by 1st1

This comment has been minimized.

Copy link
@Carreau

Carreau May 23, 2019

Contributor

Just for consistency with the normal repl I would remove the \n here.

(I'll gladly not get other blank lines in the banner, but that's personal taste)

)

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.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
@@ -0,0 +1 @@
Implement asyncio REPL

This comment has been minimized.

Copy link
@tirkarthi

tirkarthi May 24, 2019

Contributor

Can a note be added to Whats New too?

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.