Monty Rust API Reference
Pinned rev: 87f8f31 (2025 — pyo3-build-config for ubuntu tests)
Source: https://github.com/pydantic/monty.git
Entry Point: MontyRun
pub struct MontyRun { /* private */ }
impl MontyRun {
pub fn new(
code: String,
script_name: &str,
input_names: Vec<String>,
external_functions: Vec<String>,
) -> Result<Self, MontyException>
pub fn code(&self) -> &str
pub fn run(
&self,
inputs: Vec<MontyObject>,
resource_tracker: impl ResourceTracker,
print: &mut PrintWriter<'_>,
) -> Result<MontyObject, MontyException>
pub fn run_no_limits(
&self,
inputs: Vec<MontyObject>,
) -> Result<MontyObject, MontyException>
pub fn start<T: ResourceTracker>(
self, // NOTE: consumes self
inputs: Vec<MontyObject>,
resource_tracker: T,
print: &mut PrintWriter<'_>,
) -> Result<RunProgress<T>, MontyException>
pub fn dump(&self) -> Result<Vec<u8>, postcard::Error>
pub fn load(bytes: &[u8]) -> Result<Self, postcard::Error>
}
Execution Progress: RunProgress<T>
pub enum RunProgress<T: ResourceTracker> {
FunctionCall {
function_name: String,
args: Vec<MontyObject>,
kwargs: Vec<(MontyObject, MontyObject)>,
call_id: u32,
method_call: bool,
state: Snapshot<T>,
},
OsCall {
function: OsFunction,
args: Vec<MontyObject>,
kwargs: Vec<(MontyObject, MontyObject)>,
call_id: u32,
state: Snapshot<T>,
},
ResolveFutures(FutureSnapshot<T>),
Complete(MontyObject),
}
Resuming: Snapshot<T>
pub struct Snapshot<T: ResourceTracker> { /* private */ }
impl<T: ResourceTracker> Snapshot<T> {
pub fn run(
self,
result: impl Into<ExternalResult>,
print: &mut PrintWriter<'_>,
) -> Result<RunProgress<T>, MontyException>
pub fn run_pending(
self,
print: &mut PrintWriter<'_>,
) -> Result<RunProgress<T>, MontyException>
pub fn tracker_mut(&mut self) -> &mut T
}
External Results
pub enum ExternalResult {
Return(MontyObject),
Error(MontyException),
Future,
}
impl From<MontyObject> for ExternalResult { /* ... */ }
impl From<MontyException> for ExternalResult { /* ... */ }
Python Values: MontyObject
pub enum MontyObject {
Ellipsis,
None,
Bool(bool),
Int(i64),
BigInt(BigInt), // num-bigint
Float(f64),
String(String),
Bytes(Vec<u8>),
List(Vec<Self>),
Tuple(Vec<Self>),
NamedTuple { type_name: String, field_names: Vec<String>, values: Vec<Self> },
Dict(DictPairs),
Set(Vec<Self>),
FrozenSet(Vec<Self>),
Path(String),
Dataclass { name: String, type_id: u64, field_names: Vec<String>, attrs: DictPairs, frozen: bool },
Type(Type),
BuiltinFunction(BuiltinsFunctions),
Exception { exc_type: ExcType, arg: Option<String> },
Repr(String),
Cycle(HeapId, String),
}
pub struct DictPairs(Vec<(MontyObject, MontyObject)>);
Exceptions: MontyException
pub struct MontyException { /* private */ }
impl MontyException {
pub fn new(exc_type: ExcType, message: Option<String>) -> Self
pub fn exc_type(&self) -> ExcType
pub fn message(&self) -> Option<&str>
pub fn into_message(self) -> Option<String>
pub fn traceback(&self) -> &[StackFrame]
pub fn summary(&self) -> String // "Type: message"
pub fn py_repr(&self) -> String // "Type('message')"
}
pub struct StackFrame {
pub filename: String,
pub start: CodeLoc,
pub end: CodeLoc,
pub frame_name: Option<String>,
pub preview_line: Option<String>,
pub hide_caret: bool,
pub hide_frame_name: bool,
}
pub struct CodeLoc {
pub line: u16, // 1-based
pub column: u16, // 1-based
}
Resource Limits
pub trait ResourceTracker: fmt::Debug {
fn on_allocate(&mut self, get_size: impl FnOnce() -> usize) -> Result<(), ResourceError>;
fn on_free(&mut self, get_size: impl FnOnce() -> usize);
fn check_time(&self) -> Result<(), ResourceError>;
fn check_recursion_depth(&self, depth: usize) -> Result<(), ResourceError>;
fn check_large_result(&self, estimated_bytes: usize) -> Result<(), ResourceError>;
}
pub struct NoLimitTracker; // implements ResourceTracker (no-op)
pub struct LimitedTracker { /* private */ }
impl LimitedTracker {
pub fn new(limits: ResourceLimits) -> Self
pub fn allocation_count(&self) -> usize
pub fn current_memory(&self) -> usize
pub fn elapsed(&self) -> Duration
pub fn set_max_duration(&mut self, duration: Duration)
}
pub struct ResourceLimits {
pub max_allocations: Option<usize>,
pub max_duration: Option<Duration>,
pub max_memory: Option<usize>,
pub gc_interval: Option<usize>,
pub max_recursion_depth: Option<usize>,
}
impl ResourceLimits {
pub fn new() -> Self // sets max_recursion_depth: 1000
pub fn max_allocations(self, limit: usize) -> Self
pub fn max_duration(self, limit: Duration) -> Self
pub fn max_memory(self, limit: usize) -> Self
pub fn max_recursion_depth(self, limit: Option<usize>) -> Self
}
Print Output
pub enum PrintWriter<'a> {
Disabled,
Stdout,
Collect(String),
Callback(&'a mut dyn PrintWriterCallback),
}
impl PrintWriter<'_> {
pub fn stdout_write(&mut self, output: Cow<'_, str>) -> Result<(), MontyException>
pub fn collected_output(&self) -> Option<&str>
}
How Print Capture Works
The Rust FFI layer uses PrintWriter::Collect(String::new()) at every
execution site (run, start, resume). When Python code calls
print(), Monty appends the formatted output to the collected string
inside the PrintWriter.
After each execution call returns, the collected string is extracted and
appended to MontyHandle.print_output. This accumulates across multiple
start/resume steps in iterative execution, so a single
print_output string contains all output from the entire session.
When build_result_json constructs the final JSON, print_output is
included only if non-empty (omitted when there was no print output).
Data flow (native):
Python print("hello") → Monty PrintWriter::Collect → MontyHandle.print_output
→ build_result_json includes "print_output": "hello\n"
→ monty_complete_result_json() returns JSON string via C FFI
→ Dart MontyResult.fromJson reads json['print_output']
→ MontyResult.printOutput available to app code
Dart API:
final result = await Monty.exec('print("hello")\n42');
print(result.value); // 42
print(result.printOutput); // "hello\n"
printOutput is null when no print() calls were made. It contains
the full concatenated output (with newlines) when print() was called.
JSON Contract (C FFI to Dart)
All JSON must match Dart fromJson factories exactly (snake_case keys):
| Dart type | JSON shape |
|---|---|
MontyResult |
{ "value": ..., "error": {...}?, "usage": {...}, "print_output": "..."? } |
MontyException |
{ "message": "...", "filename": "..."?, "line_number": N?, "column_number": N?, "source_code": "..."? } |
MontyResourceUsage |
{ "memory_bytes_used": N, "time_elapsed_ms": N, "stack_depth_used": N } |
MontyProgress |
discriminated by "type": "complete" or "pending" |
MontyComplete |
{ "type": "complete", "result": { MontyResult } } |
MontyPending |
{ "type": "pending", "function_name": "...", "arguments": [...] } |
MontyObject to JSON Mapping
| MontyObject variant | JSON |
|---|---|
None |
null |
Bool(b) |
true / false |
Int(n) |
number |
BigInt(n) |
number if fits i64, else string |
Float(f) |
number |
String(s) |
string |
List(v) / Tuple(v) |
array |
Dict(pairs) |
object (string keys) or array of pairs |
Ellipsis |
"..." |
Bytes(v) |
base64 string or array of ints |
Set(v) / FrozenSet(v) |
array |