Skip to content

Latest commit

 

History

History
212 lines (157 loc) · 5.7 KB

File metadata and controls

212 lines (157 loc) · 5.7 KB

Embedded Python (Monty)

Experimental. Monty is an early-stage Python interpreter that may have undiscovered crash or security bugs. Resource limits are enforced by Monty's runtime. The integration should be treated as experimental.

Bashkit embeds the Monty Python interpreter, a pure-Rust implementation of Python 3.12. Python runs entirely in-memory with configurable resource limits and no host access.

See also:

Quick Start

Enable the python feature and register via builder:

use bashkit::Bash;

# #[tokio::main]
# async fn main() -> bashkit::Result<()> {
let mut bash = Bash::builder()
    .python()
    .env("BASHKIT_ALLOW_INPROCESS_PYTHON", "1")
    .build();

let result = bash.exec("python3 -c \"print('hello from Monty')\"").await?;
assert_eq!(result.stdout, "hello from Monty\n");
# Ok(())
# }

Usage Patterns

Inline Code

python3 -c "print(2 ** 10)"
# Output: 1024

Expression Evaluation

When no print() is called, the last expression is displayed (REPL behavior):

python3 -c "2 + 2"
# Output: 4

Script Files (from VFS)

cat > /tmp/script.py << 'EOF'
data = [1, 2, 3, 4, 5]
print(f"sum={sum(data)}, avg={sum(data)/len(data)}")
EOF
python3 /tmp/script.py

Pipelines and Command Substitution

result=$(python3 -c "print(42 * 3)")
echo "Result: $result"

echo "print('piped')" | python3

Virtual Filesystem (VFS) Bridging

Python pathlib.Path operations are bridged to Bashkit's virtual filesystem. Files created by bash are readable from Python and vice versa.

Bash → Python

echo "important data" > /tmp/shared.txt
python3 -c "
from pathlib import Path
content = Path('/tmp/shared.txt').read_text()
print(f'Got: {content.strip()}')
"

Python → Bash

python3 -c "
from pathlib import Path
_ = Path('/tmp/result.txt').write_text('computed by python\n')
"
cat /tmp/result.txt

Supported Path Operations

Operation Example
Read text Path('f.txt').read_text()
Read bytes Path('f.txt').read_bytes()
Write text Path('f.txt').write_text('data')
Write bytes Path('f.txt').write_bytes(b'data')
Exists Path('f.txt').exists()
Is file/dir Path('f.txt').is_file(), .is_dir()
Mkdir Path('d').mkdir(parents=True, exist_ok=True)
Delete Path('f.txt').unlink()
List dir Path('.').iterdir()
Stat Path('f.txt').stat().st_size
Rename Path('old').rename('new')
Env vars os.getenv('KEY'), os.environ

Architecture

Python code → Monty VM → OsCall(ReadText, path) → Bashkit VFS → resume

Monty pauses at filesystem operations, Bashkit bridges them to the VFS, then resumes execution with the result (or a Python exception like FileNotFoundError).

Resource Limits

Default limits prevent runaway Python code. Customize via PythonLimits:

use bashkit::{Bash, PythonLimits};
use std::time::Duration;

# fn main() {
let bash = Bash::builder()
    .python_with_limits(
        PythonLimits::default()
            .max_duration(Duration::from_secs(5))
            .max_memory(16 * 1024 * 1024)   // 16 MB
            .max_allocations(100_000)
            .max_recursion(50)
    )
    .build();
# }
Limit Default Purpose
Allocations 1,000,000 Heap allocation cap
Duration 30 seconds Execution timeout
Memory 64 MB Heap memory cap
Recursion 200 Call stack depth

LLM Tool Integration

When using BashTool for AI agents, call .python() on the tool builder:

use bashkit::{BashTool, Tool};

# fn main() {
let tool = BashTool::builder()
    .python()
    .build();

// help() and system_prompt() automatically document Python limitations
let help = tool.help();  // Includes a Markdown Notes section with Python hints
# }

The builtin's llm_hint() is automatically included in the tool's documentation, so LLMs know not to generate code using open(), HTTP requests, or classes.

Limitations

No open() builtin. Monty does not implement Python's open(). Use pathlib.Path instead:

# Won't work:
# f = open('data.txt')

# Use instead:
from pathlib import Path
content = Path('data.txt').read_text()

No HTTP/network. No socket, urllib, requests, or http.client modules. Monty has no network primitives and no OsCall variants for network operations.

No classes. Class definitions are not yet supported by Monty (planned upstream).

No third-party imports. Only builtin modules (sys, typing, os, pathlib, math, json, datetime) are available. No pip install, no import numpy.

No re module. Disabled due to catastrophic backtracking DoS risk in untrusted code execution.

No str.format(). Use f-strings instead: f"value={x}" not "value={}".format(x).

Security

All Python execution runs in a virtual environment:

  • No host filesystem access — all paths resolve through the VFS
  • No network access — no sockets, HTTP, or DNS
  • No process spawning — no os.system(), subprocess, or __import__('os')
  • Resource limited — allocation, time, memory, and recursion caps
  • Path traversal safe../.. is resolved by VFS path normalization

See threat IDs TM-PY-001 through TM-PY-029 in the threat model.