Source code for code_execution
import subprocess
import warnings
import tempfile
import concurrent.futures
[docs]
plt_code = """import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
fig, ax = plt.subplots()
fruits = ['apple', 'blueberry', 'cherry', 'orange']
counts = [40, 100, 30, 55]
bar_labels = ['red', 'blue', '_red', 'orange']
bar_colors = ['tab:red', 'tab:blue', 'tab:red', 'tab:orange']
ax.bar(fruits, counts, label=bar_labels, color=bar_colors)
ax.set_ylabel('fruit supply')
ax.set_title('Fruit supply by kind and color')
ax.legend(title='Fruit color')
plt.show()"""
[docs]
def execute_plt_test():
with concurrent.futures.ThreadPoolExecutor() as executor:
try:
future = executor.submit(execute_code, plt_code)
return future.result()
except Exception as e:
print(f"execute_plt_test caught error: {str(e)}")
[docs]
def execute_code(final_code: str, open_code=False, log = print):
"""
Executes the code provided in the `final_code` string, returning `True` if the execution was successful,
of `False` if an unhandled exception was raised or the execution resulted in an error code being returned.
Current implementation writes the code to a temporary file and runs it using `subprocess`.
This detail could change; this function is a wrapper around the current implementation.
We may for example sandbox the code in a separate container, or use a different method of execution.
Args:
final_code: The code to be executed
open_code: For local development, if true will open the code in Visual Studio Code
log: A callable that will be used to log messages
"""
# exec(final_code) - I don't understand why this doesn't work
# "name 'MDVProject' is not defined"
with tempfile.NamedTemporaryFile(suffix=".py") as temp:
log(f"Writing to temp file: {temp.name}")
temp.write(final_code.encode())
temp.flush()
if open_code:
subprocess.run(["code", temp.name])
# try:
# subprocess.run(["python", temp.name])
# except subprocess.CalledProcessError as e:
# log(f"CalledProcessError: {str(e)}")
# except Exception as e:
# log(f"Error: {str(e)}")
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
stdout, stderr = run_subprocess(["python", temp.name])
if stderr:
log(f"Standard Error: {stderr}")
for warning in w:
if issubclass(warning.category, UserWarning):
log(f"UserWarning encountered: {warning.message}")
if stdout is not None:
log(f"Standard Output: {stdout}")
return True, stdout, stderr
else:
log("--- Execution failed ---")
log(final_code)
return False, stdout, stderr
[docs]
def run_subprocess(command: list[str]) -> tuple[str | None, str]:
try:
# Capture standard output and standard error
result = subprocess.run(
command,
# shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
# text=True
)
return result.stdout.decode(), result.stderr.decode()
except subprocess.CalledProcessError as e:
# Handle subprocess errors
print(f"Subprocess error: {e}")
print(f"Standard Output: {e.stdout}")
print(f"Standard Error: {e.stderr}")
return None, e.stderr.decode()
except Exception as e:
# Handle other exceptions
print(f"General error: {e}")
return None, str(e)