Welcome to asyncode’s documentation!¶
The goal of this module is to allow to emulate Python’s interactive interpreter in already running asnychronous contexts.
For example, it allows to debug a Discord bot (built with Discord.py, that uses async mechanisms) directly inside Discord client, by processing messages sent in a channel as interpreter commands.
This module source code is based on code
standard module by Guido van
Rossum and co-othors, and on asyncio.__main__ code by Yury Selivanov.
It differs from the latter in that its classes operate in already running
asynchronous contexts, while running python -m asyncio
create and run a
new asyncio loop.
Rationale¶
Several issues prevent directly using Python’s code
standard module :
code.InteractiveConsole
andcode.InteractiveInterpreter
methods are not asynchronous, so the interpreter will be blocking the loop between two inputs: one solution would be to run it in a specific thread, but this adds a lot of complexity and potential error sources;the code compiler (
codeop.CommandCompiler
instance) disallow usingawait
statements outside of functions, so it would be very unpleasant to await coroutines inside the interpreter (declare a asynchronous function and useasyncio.get_running_loop()
);information printed by
print()
,repr()
… (like when sending an object name) tosys.stdout
andsys.stderr
would be lost, as we need an asynchronous function to send content to the interpreter.
Implementation¶
This module provides two classes, AsyncInteractiveInterpreter
and
AsyncInteractiveConsole
, who do the exact same job as standard
code.InteractiveInterpreter
and code.InteractiveConsole
,
except that:
write()
,raw_input()
and other higher-level methods are asynchronous (they return coroutines, that have to be awaited).Warning
Only text input/output is asnychronous: in particular, code executed in the interpreter will still be blocking.
For example, sending
time.sleep(10)
in a runningAsyncInteractiveConsole
will block every other concurrent task for 10 seconds.the compiler allows top-level
await
statements;instead of being directly executed, compiled code is wrapped in a function object (
types.FunctionType
) which is called: if the result is a coroutine, it is then awaited; otherwise, this is strictly identical to direct code execution.sys.stdout
andsys.stderr
are catch during code execution, then gathered content is written in the interpreter. This behavior can be turned off withreroute_stdout
andreroute_stderr
arguments.
Usage¶
asyncode
is meant to be used in existing asynchronous environments.
Minimal working code will need to subclass provided classes to implement
appropriate functions:
import asyncode
class MyAsyncConsole(asyncode.AsyncInteractiveConsole):
"""AsyncInteractiveConsole adapted to running environment"""
async def write(self, data):
"""Use appropriate method"""
await some_output_coroutine(data)
async def raw_input(self, prompt=""):
"""Use appropriate method"""
if prompt:
await some_output_coroutine(prompt)
data = await some_input_coroutine()
return data
async def run_interpreter():
"""Run an interactive Python interpreter"""
console = MyAsyncConsole()
try:
await console.interact()
except SystemExit:
# Do not exit the whole program!
await some_output_coroutine("Bye!")
Warning
As for code
classes, SystemExit
exceptions are not
catched by provided methods. This mean that quit()
/exit()
commands
run inside the console will quit your whole program if not explicitly
catched (as in this example), which may not be what you want.
Note
High-level async environments may not provide a way to send EOF or KeyboardInterrupt signals to the console. Custom implementations should not forget a exit mechanism (like recognize some custom message), or just catch SystemExit (as in this example).
API Reference¶
Emulating Python’s interactive interpreter in asynchronous contexts.
-
class
asyncode.
AsyncInteractiveInterpreter
(locals, *, reroute_stdout=True, reroute_stderr=True)[source]¶ Base class for AsyncInteractiveConsole.
- This class inherits from
code.InteractiveInterpreter
. It makes
runsource
,runcode
,showsyntaxerror
,showtraceback
andwrite
methods asynchronous (they return coroutines, that have to be awaited);tell the compiler to allow top-level
await
statements;awaits coroutines returned by such statements execution;
catches
sys.stdout
andsys.stderr
during code execution, then send gathered content usingwrite()
.
This class deals with parsing and interpreter state (the user’s namespace); it doesn’t deal with input buffering or prompting or input file naming (the filename is always passed in explicitly).
-
__init__
(locals, *, reroute_stdout=True, reroute_stderr=True)[source]¶ Constructor.
The optional
locals
argument specifies the dictionary in which code will be executed; it defaults to a newly created dictionary with key"__name__"
set to"__console__"
and key"__doc__"
set toNone
.The optional
reroute_stdout
andreroute_stderr
keyword arguments, if set toFalse
, disable catchingsys.stdout
andsys.stderr
, respectively.
-
async
runsource
(source, filename='<input>', symbol='single')[source]¶ Compile and run some source in the interpreter.
Arguments and behavior are as for
code.InteractiveInterpreter
, except that this is an asynchronous method.Unless explicitly disabled, this method catches
sys.stdout
andsys.stderr
during code execution. If some data has been written, it is send usingwrite()
before returning.
-
async
runcode
(code)[source]¶ Execute a code object.
Arguments and behavior are as for
code.InteractiveInterpreter
, except that this is an asynchronous method and that ‘code’ is wrapped in a function object which is called instead of being directly executed.If the result is a coroutine, it is then awaited before this method returns. Exceptions are processed in the same way as during code execution.
-
async
showsyntaxerror
(filename=None)[source]¶ Display the syntax error that just occurred.
Arguments and behavior are as for
code.InteractiveInterpreter
, except that this is an asynchronous method.
-
async
showtraceback
()[source]¶ Display the exception that just occurred.
Arguments and behavior are as for
code.InteractiveInterpreter
, except that this is an asynchronous method.
-
async
write
(data)[source]¶ Write a string.
Arguments are as for
code.InteractiveInterpreter
. While that this is an async method, the base implementation still writes tosys.stderr
; a subclass should replace this with a different (asynchronous) implementation.
- This class inherits from
-
class
asyncode.
AsyncInteractiveConsole
(locals=None, filename='<console>', *, reroute_stdout=True, reroute_stderr=True)[source]¶ Emulate interactive Python interpreter in asynchronous contexts.
This class builds on
AsyncInteractiveInterpreter
and adds prompting using the familiarsys.ps1
andsys.ps2
, and input buffering.It does exactly the same job as
code.InteractiveConsole
, except thatinteract
,push
andraw_input
methods are asynchronous (they return coroutines, that have to be awaited).-
__init__
(locals=None, filename='<console>', *, reroute_stdout=True, reroute_stderr=True)[source]¶ Constructor.
The optional
locals
,reroute_stdout
andreroute_stderr
arguments will be passed to theAsyncInteractiveInterpreter
base class. The optionalfilename
argument should specify the (file)name of the input stream; it will show up in tracebacks.
-
async
interact
(banner=None, exitmsg=None)[source]¶ Closely emulate the interactive Python console.
Arguments and behavior are as for
code.InteractiveConsole
, except that this is an asynchronous method that awaits inputs.
-
async
push
(line)[source]¶ Push a line to the interpreter.
Arguments and behavior are as for
code.InteractiveConsole
, except that this is an asynchronous method.
-
async
raw_input
(prompt='')[source]¶ Write a prompt and read a line.
Arguments and behavior are as for
code.InteractiveConsole
. While that this is an async method, the base implementation still uses the built-in functioninput()
; a subclass should replace this with a different (asynchronous) implementation.
-