# Build a stateful, sandboxed code interpreter

This example demonstrates how to build a stateful code interpreter using a Modal
[Sandbox](https://modal.com/docs/guide/sandbox).

We'll create a Modal Sandbox that listens for code to execute and then
executes the code in a Python interpreter. Because we're running in a sandboxed
environment, we can safely use the "unsafe" `exec()` to execute the code.

## Setting up a code interpreter in a Modal Sandbox

Our code interpreter uses a Python "driver program" to listen for code
sent in JSON format to its standard input (`stdin`), execute the code,
and then return the results in JSON format on standard output (`stdout`).

```python
import inspect
import json
import sys
from typing import Any, Iterator

import modal


def driver_program():
    import json
    import sys
    from contextlib import redirect_stderr, redirect_stdout
    from io import StringIO

    # When you `exec` code in Python, you can pass in a dictionary
    # that defines the global variables the code has access to.

    # We'll use that to store state.

    globals: dict[str, Any] = {}
    while True:
        command = json.loads(input())  # read a line of JSON from stdin
        if (code := command.get("code")) is None:
            print(json.dumps({"error": "No code to execute"}))
            continue

        # Capture the executed code's outputs
        stdout_io, stderr_io = StringIO(), StringIO()
        with redirect_stdout(stdout_io), redirect_stderr(stderr_io):
            try:
                exec(code, globals)
            except Exception as e:
                print(f"Execution Error: {e}", file=sys.stderr)

        print(
            json.dumps(
                {"stdout": stdout_io.getvalue(), "stderr": stderr_io.getvalue()}
            ),
            flush=True,
        )


```

We run this driver program in a [Modal Sandbox](https://modal.com/docs/guide/sandboxes).

```python
app = modal.App.lookup("example-simple-code-interpreter", create_if_missing=True)
sb = modal.Sandbox.create(app=app)

```

We have to convert the driver program to a string to pass it to the Sandbox.
Here we use `inspect.getsource` to get the source code as a string,
but you could also keep the driver program in a separate file and read it in.

```python
driver_program_text = inspect.getsource(driver_program)
driver_program_command = f"""{driver_program_text}\n\ndriver_program()"""

```

We then kick off the program with [`Sandbox.exec`](https://modal.com/docs/reference/modal.Sandbox#exec),
which creates a process inside the Sandbox (see [`modal.container_process`](https://modal.com/docs/reference/modal.container_process)
for details).

```python
p = sb.exec("python", "-c", driver_program_command, bufsize=1)

```

## Running code in a Modal Sandbox

Now we need a way to run code inside that running driver process.
Our driver program already defined a JSON interface on its `stdin` and `stdout`,
so we just need to write a quick wrapper to write to the remote `stdin`
and read from the remote `stdout`.

```python
reader, writer = p.stdin, iter(p.stdout)


def run_code(writer: modal.io_streams.StreamWriter, reader: Iterator[str], code: str):
    writer.write(json.dumps({"code": code}) + "\n")
    writer.drain()
    result = json.loads(next(reader))
    print(result["stdout"], end="")
    if result["stderr"]:
        print("\033[91m" + result["stderr"] + "\033[0m", end="", file=sys.stderr)


```

Now we can execute some code in the Sandbox!

```python
run_code(reader, writer, "print('hello, world!')")  # hello, world!

```

The Sandbox and our code interpreter are stateful,
so we can define variables and use them in subsequent code.

```python
run_code(reader, writer, "x = 10")
run_code(reader, writer, "y = 5")
run_code(reader, writer, "result = x + y")
run_code(reader, writer, "print(f'The result is: {result}')")  # The result is: 15

```

We can also see errors when code fails.

```python
run_code(reader, writer, "print('Attempting to divide by zero...')")
run_code(reader, writer, "1 / 0")  # Execution Error: division by zero

```

Finally, let's clean up after ourselves and terminate the Sandbox.

```python
sb.terminate()

```
