Run Python code within Python with best practices. Designed for educational use such as futurecoder and Papyros.
- Saves source code in a file (if possible) and
linecache
so it can be accessed by tracebacks,inspect
,pdb
,doctest
, etc - Runs code in a fresh module where
__name__ == "__main__"
- Allows easy and correct overriding of
sys.stdin/stdout/stderr
andtime.sleep
- Show full tracebacks with internals hidden
- Support for running async code
- Single callback to handle different kinds of events and output
- Output buffering
- Integration with
snoop
for debugging - Integration with
pyodide-worker-runner
from python_runner import PatchedStdinRunner
def get_input(prompt):
return "some input"
def send_output(part_type, text):
if part_type == "stdout":
color = "white"
elif part_type in ("stderr", "traceback", "syntax_error"):
color = "red"
else:
color = "blue"
show_output(color, text)
def callback(event_type, data):
if event_type == "input":
return get_input(data["prompt"])
else:
assert event_type == "output"
for part in data["parts"]:
send_output(part["type"], part["text"])
runner = PatchedStdinRunner(callback=callback)
code = """
name = input("Who are you?")
print(f"Hello {name}!")
"""
runner.run(code)
# Calls:
# get_input("Who are you?")
# send_output("input_prompt", "Who are you?")
# send_output("input", "some input")
# send_output("stdout", "Hello some input!")
# send_output("stdout", "\n")
Runner
is the simplest runner. It patchessys.stdout
andsys.stderr
.PatchedStdinRunner
also patchessys.stdin
PatchedSleepRunner
also patchestime.sleep
PyodideRunner
inheritsPatchedStdinRunner
andPatchedSleepRunner
and is meant to be used in combination withpyodide-worker-runner
, especiallymakeRunnerCallback
.
Before calling .run()
or .run_async()
, you must pass a callback
to the constructor or .set_callback()
:
def callback(event_type, data):
...
runner = Runner(callback=callback)
# or
runner = Runner()
runner.set_callback(callback)
data
is a dict with string keys and arbitrary values. Custom calls to runner.callback()
can pass any values, but standard values of event_type
are:
input
:- When using
PatchedStdinRunner
data
will have one keyprompt
which will contain the prompt passed to theinput
function, defaulting to the empty string.- The callback should return a string representing one line of user input.
- When using
output
:data
will have one keyparts
which will contain a list of output parts. Each part is a dict. The dict may contain anything, but it must contain at least these two keys:text
: a string containing the outputtype
: a code describing the type of output. Custom calls torunner.output()
can pass any values, but standard values oftype
are:stdout
: for strings written tosys.stdout
.stderr
: for strings written tosys.stderr
.traceback
: when code passed to.run()
or.run_async()
raises an error at runtime, this part will contain the return value of.serialize_traceback()
.syntax_error
: when code passed to.run()
or.run_async()
has a syntax error and fails to compile, this part will contain the return value of.serialize_syntax_error()
.input_prompt
: the prompt passed to theinput()
function. Only created whenPatchedStdinRunner
is used.input
: the user's input passed to stdin. Not actually output, but included here because it's typically shown in regular Python consoles. Only created whenPatchedStdinRunner
is used.snoop
: debugging output fromsnoop
when.run()
is called withmode='snoop'
.
sleep
:- When using
PatchedSleepRunner
data
will have one keyseconds
which will contain the number of seconds to sleep for.
- When using
Call runner.run()
or await runner.run_async()
with a string containing Python source code. You can also pass the following optional keyword arguments:
mode
:'exec'
: (default) for running multiple statements normally, like theexec()
builtin function.'eval'
: for evaluating the value of a single expression, like theeval()
builtin function. In this case,.run()
or.run_async()
will return the evaluated expression if successful.'single'
: for running a single statement, printing the value if it's an expression, like the standard Python REPL.'snoop'
: like'exec'
but runs the code with the snoop debugger (installed separately).
snoop_config
: only used whenmode='snoop'
. A dict which will be used as keyword arguments for asnoop.Config
object. For example,snoop_config=dict(color='monokai')
will enable ANSI color codes in the debugging output.top_level_await
: only for.run_async()
. If true (the default), the given source code can use theawait
keyword at the top level outside of a function.