71 lines
2.5 KiB
Python

import subprocess
from pathlib import Path
from backend.managers.debug_logger import get_logger
logger = get_logger(__name__)
# Maximum time (seconds) a subprocess is allowed to run before being killed.
RUN_TIMEOUT = 30
class ExecutionEngine:
"""Runs files from the editor in a subprocess and returns the output.
Currently supports Python (.py) and LaTeX (.tex) files.
Returns a dict with keys: stdout, stderr, rc (return code).
"""
def __init__(self):
pass
def run_code(self, active_file: Path) -> dict:
"""Execute the given file and return its output.
Args:
active_file: Absolute path to the file that should be run.
Returns:
{"stdout": str, "stderr": str, "rc": int}
rc == 0 means success, anything else is an error.
"""
suffix = active_file.suffix
current_dir = active_file.parent.resolve()
# Build the shell command depending on file type
if suffix == ".py":
cmd = ["py", active_file.name]
elif suffix == ".tex":
# pdflatex in non-interactive mode so it never waits for input
cmd = [
"pdflatex",
"-interaction=nonstopmode",
f"-output-directory={current_dir}",
active_file.name,
]
else:
return {"stdout": "", "stderr": f"Unsupported file type: {suffix}", "rc": 1}
logger.info("Running file %s with suffix %s", active_file.name, suffix)
try:
proc = subprocess.run(
cmd,
cwd=current_dir, # run inside the file's own directory
capture_output=True,
text=True,
timeout=RUN_TIMEOUT,
)
logger.info("File ran successfully.")
return {"stdout": proc.stdout, "stderr": proc.stderr, "rc": proc.returncode}
except subprocess.TimeoutExpired:
logger.warning("Time out afte %s s", RUN_TIMEOUT)
return {"stdout": "", "stderr": f"Timed out after {RUN_TIMEOUT}s", "rc": -1}
except FileNotFoundError as e:
# Raised when the interpreter/compiler binary is not found on PATH
logger.warning("Interpreter/compiler binary is not found on PATH: %s", e)
return {"stdout": "", "stderr": str(e), "rc": -1}
except Exception as e:
logger.exception("Error while running %s: %s", active_file.name, e)
return {"stdout": "", "stderr": str(e), "rc": -1}