import io | |
import sys | |
import ast | |
from logger import logger | |
def run_code(code): | |
""" | |
Executes user-provided Python code and captures its output. | |
Handles user input dynamically and detects defined functions. | |
Parameters: | |
code (str): Python code entered by the user. | |
Returns: | |
dict: Captured output and logged inputs. | |
""" | |
# Redirect stdout to capture code output | |
old_stdout = sys.stdout | |
redirected_output = sys.stdout = io.StringIO() | |
# Create a dedicated execution namespace | |
exec_globals = {} | |
# Store user inputs | |
input_log = [] | |
# Custom input function to simulate input() behavior | |
def custom_input(prompt=""): | |
user_input = input(prompt) # Prompt the user dynamically | |
input_log.append(user_input) | |
return user_input | |
# Add the custom input function to the execution namespace | |
exec_globals["input"] = custom_input | |
try: | |
# Parse the code to detect function definitions | |
tree = ast.parse(code) | |
function_names = [ | |
node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef) | |
] | |
# Execute the code in the dedicated namespace | |
exec(code, exec_globals) | |
# Check if functions are defined but not called | |
captured_output = redirected_output.getvalue() | |
if function_names: | |
captured_output += f"\n\nDefined functions: {', '.join(function_names)}\n" | |
captured_output += "Note: Functions need to be explicitly called to see their output." | |
return {"output": captured_output, "inputs": input_log} | |
except Exception as e: | |
logger.error(f"Execution error: {e}") | |
return {"output": f"Error: {e}", "inputs": input_log} | |
finally: | |
# Reset stdout | |
sys.stdout = old_stdout | |