Files
QPP/utils.py

100 lines
3.2 KiB
Python

import sys
import traceback
import time
import io
import tempfile
import subprocess
import os
def run_code_against_tests(user_code, test_code):
local_ns = {}
output = ''
start = time.perf_counter()
error = None
passed = False
temp_file = None
try:
# Check if unittest is used in test_code
if 'unittest' in test_code:
# Write user code + test code to a temp file
with tempfile.NamedTemporaryFile('w+', suffix='.py', delete=False, encoding='utf-8') as f:
combined_code = f"{user_code}\n\n{test_code}"
f.write(combined_code)
f.flush()
temp_file = f.name
# Run the file as a subprocess
try:
proc = subprocess.run(
[sys.executable, temp_file],
capture_output=True,
text=True,
timeout=10,
encoding='utf-8'
)
output = proc.stdout
if proc.stderr:
output += f"\n{proc.stderr}"
passed = proc.returncode == 0
if not passed:
error = f"Tests failed. Return code: {proc.returncode}\n{output}"
else:
# For successful unittest runs, the stderr contains the test results
if proc.stderr and "OK" in proc.stderr:
output = proc.stderr # Use stderr as the main output for unittest
except subprocess.TimeoutExpired:
passed = False
error = "Code execution timed out after 10 seconds"
output = "Execution timed out"
else:
# Capture stdout
old_stdout = sys.stdout
captured_output = io.StringIO()
sys.stdout = captured_output
try:
# Execute user code
exec(user_code, {}, local_ns)
# Execute test code (should raise AssertionError if fail)
exec(test_code, local_ns, local_ns)
passed = True
except AssertionError as e:
passed = False
error = f"Assertion failed: {str(e)}"
except Exception as e:
passed = False
error = f"Runtime error: {traceback.format_exc()}"
finally:
output = captured_output.getvalue()
sys.stdout = old_stdout
except Exception as e:
passed = False
error = f"Execution error: {traceback.format_exc()}"
finally:
# Clean up temporary file
if temp_file and os.path.exists(temp_file):
try:
os.unlink(temp_file)
except Exception as e:
print(f"Warning: Could not delete temp file {temp_file}: {e}")
runtime = time.perf_counter() - start
result = {
'passed': passed,
'output': output.strip() if output else '',
'runtime': runtime,
'error': error if not passed else None
}
return result