Quick Start Guide

The simple-code-execution library provides a powerful framework for executing code predictions in parallel with comprehensive logging, file management, and result processing.

Installation

git clone https://github.com/gabeorlanski/simple-code-execution.git
cd simple-code-execution
pip install -e .

Basic Usage

The library follows a simple workflow: preprocessexecutepostprocess. Here’s a minimal example that demonstrates executing Python code snippets:

from code_execution import ExecutionConfig, execute_predictions, Executable, Command

# Define your predictions (code to execute)
predictions = [
    {"id": 1, "code": "print('Hello, World!')"},
    {"id": 2, "code": "x = 5\nprint(x * 2)"},
    {"id": 3, "code": "import math\nprint(math.sqrt(16))"},
]

# Configure execution settings
config = ExecutionConfig(
    num_workers=2,           # Number of parallel workers
    default_timeout=10,      # Timeout in seconds
    max_execute_at_once=3    # Max concurrent executions
)

# Define preprocessor: converts predictions to executable commands
# IMPORTANT: Must be pickleable (serializable) for multiprocessing
def preprocessor(prediction):
    return Executable(
        files={"main.py": prediction["code"]},  # Files to write
        commands=[Command(command=["python3", "main.py"])],  # Commands to run
        tracked_files=[]  # Files to read back after execution
    )

# Define postprocessor: processes execution results
# IMPORTANT: Must be pickleable (serializable) for multiprocessing
def postprocessor(prediction, result):
    return {
        "id": prediction["id"],
        "code": prediction["code"],
        "output": result.command_results[0].stdout,
        "success": result.command_results[0].return_code == 0,
        "runtime": result.command_results[0].runtime
    }

# Execute all predictions
results = execute_predictions(
    config=config,
    pred_list=predictions,
    preprocessor=preprocessor,
    postprocessor=postprocessor
)

# Print results
for result in results.results:
    print(f"ID: {result['id']}")
    print(f"Output: {result['output'].strip()}")
    print(f"Success: {result['success']}")
    print(f"Runtime: {result['runtime']:.3f}s")
    print("-" * 40)

Expected Output:

ID: 1
Output: Hello, World!
Success: True
Runtime: 0.045s
----------------------------------------
ID: 2
Output: 10
Success: True
Runtime: 0.043s
----------------------------------------
ID: 3
Output: 4.0
Success: True
Runtime: 0.051s
----------------------------------------

Key Components

ExecutionConfig

Configuration object that controls execution behavior including parallelism, timeouts, and resource limits.

Executable

Data structure containing files to write, commands to execute, and files to track after execution.

Command

Represents a single command with its arguments, timeout, and execution parameters.

Preprocessor Function

Converts your prediction data into Executable objects that the library can run. Must be pickleable (serializable) as it runs in separate processes.

Postprocessor Function

Processes execution results and combines them with original predictions for final output. Must be pickleable (serializable) as it runs in separate processes.

Important

Pickleable Functions Required

Both preprocessor and postprocessor functions must be pickleable (serializable with Python’s pickle module) because the library uses multiprocessing for parallel execution. This means:

  • Use regular functions, not lambdas or nested functions

  • Avoid closures that capture local variables

  • Don’t use instance methods from complex objects

  • All imported modules must be available in worker processes

Good:

def my_preprocessor(prediction):
    return Executable(...)

Bad:

# Lambda - not pickleable
preprocessor = lambda pred: Executable(...)

# Nested function - not pickleable
def outer():
    def preprocessor(pred):
        return Executable(...)
    return preprocessor

Advanced Features

Multiple Commands per Prediction

def advanced_preprocessor(prediction):
    return Executable(
        files={
            "setup.py": "# Setup code",
            "main.py": prediction["code"]
        },
        commands=[
            Command(command=["python3", "setup.py"]),
            Command(command=["python3", "main.py"], timeout=5)
        ],
        tracked_files=["output.txt"]  # Read this file after execution
    )

Error Handling

The library automatically handles timeouts, syntax errors, and runtime failures. Check result.had_error and result.timed_out properties for execution status.

Parallel Execution Control

Use max_execute_at_once to limit concurrent executions and prevent resource exhaustion:

config = ExecutionConfig(
    num_workers=4,
    max_execute_at_once=10,  # Execute max 10 predictions simultaneously
    default_timeout=30
)

Next Steps

  • Explore the api_reference for complete API documentation

  • Check out advanced_usage for complex execution scenarios

  • See configuration for all available configuration options