alpha / managers /execution /python_executor.py
yhzhang3's picture
first commit
f580ad3
"""
Python Executor - Persistent Python execution environment.
Similar to Jupyter kernel or smolagents LocalPythonExecutor.
"""
import io
import sys
from typing import Any, Dict
__all__ = ["PythonExecutor"]
class PythonExecutor:
"""
Python execution environment similar to smolagents LocalPythonExecutor.
Maintains persistent state and tool namespace across executions.
"""
def __init__(self):
self.namespace = {}
self.reset_environment()
def reset_environment(self):
"""Reset the execution environment."""
# Start with basic Python builtins
self.namespace = {
"__builtins__": __builtins__,
}
# Add print output capture
self.namespace["_print_outputs"] = []
# Override print to capture outputs
def captured_print(*args, **kwargs):
output = " ".join(str(arg) for arg in args)
self.namespace["_print_outputs"].append(output)
print(*args, **kwargs) # Also print to console
self.namespace["print"] = captured_print
# Pre-import commonly used libraries
try:
import pandas as pd
import numpy as np
import os
from pathlib import Path
self.namespace["pd"] = pd
self.namespace["np"] = np
self.namespace["os"] = os
self.namespace["Path"] = Path
# Add file I/O helper functions
def write_text_file(filepath, content):
"""Write text content to a file."""
with open(filepath, 'w') as f:
f.write(content)
return f"File written to {filepath}"
def write_dataframe_to_csv(df, filepath, **kwargs):
"""Write pandas DataFrame to CSV file."""
df.to_csv(filepath, **kwargs)
return f"DataFrame written to {filepath}"
def write_dataframe_to_tsv(df, filepath, **kwargs):
"""Write pandas DataFrame to TSV file."""
df.to_csv(filepath, sep='\t', **kwargs)
return f"DataFrame written to {filepath}"
def create_directory(dirpath):
"""Create directory if it doesn't exist."""
Path(dirpath).mkdir(parents=True, exist_ok=True)
return f"Directory created: {dirpath}"
self.namespace["write_text_file"] = write_text_file
self.namespace["write_dataframe_to_csv"] = write_dataframe_to_csv
self.namespace["write_dataframe_to_tsv"] = write_dataframe_to_tsv
self.namespace["create_directory"] = create_directory
except ImportError as e:
print(f"Warning: Could not import library: {e}")
def send_functions(self, functions: Dict[str, Any]):
"""Inject functions into the execution namespace."""
for func_name, func in functions.items():
self.namespace[func_name] = func
def send_tools(self, tools: Dict[str, Any]):
"""Legacy method for backward compatibility - redirects to send_functions."""
self.send_functions(tools)
def send_variables(self, variables: Dict[str, Any]):
"""Inject variables into the execution namespace."""
if variables:
print(f"πŸ“ Injecting {len(variables)} variables into Python executor...")
self.namespace.update(variables)
def __call__(self, code: str) -> Any:
"""Execute code in the persistent namespace."""
return self.execute(code)
def execute(self, code: str) -> Any:
"""
Execute Python code in the persistent environment.
Similar to smolagents python execution.
"""
try:
# Clear previous print outputs
self.namespace["_print_outputs"] = []
# Capture stdout
old_stdout = sys.stdout
sys.stdout = captured_output = io.StringIO()
try:
# Execute the code in our persistent namespace
exec(code, self.namespace)
# Get any stdout output
stdout_output = captured_output.getvalue()
# Get print outputs from our custom print function
print_outputs = self.namespace.get("_print_outputs", [])
# Combine outputs
all_outputs = []
if stdout_output.strip():
all_outputs.append(stdout_output.strip())
if print_outputs:
all_outputs.extend(print_outputs)
result = "\n".join(all_outputs) if all_outputs else "Code executed successfully"
return result
finally:
sys.stdout = old_stdout
except Exception as e:
return f"Error: {str(e)}"