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:
- Threat Model - Security considerations (TM-PY-*)
- Custom Builtins - Writing your own builtins
- Compatibility Reference - Bash feature support
specs/python-builtin.md- Full specification
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.