Skip to content

monty

The in-process interpreter: compile, run, suspend and resume sandboxed Python inside your own process. Most hosts should use monty-pool instead, which keeps a sandbox crash from taking the host process down.

MontyRun

pub struct MontyRun { /* private fields */ }

Primary interface for running Monty code.

MontyRun supports two execution modes:

  • Simple execution: Use run or run_no_limits to run code to completion
  • Iterative execution: Use start to start execution which will pause at external function calls and can be resumed later

Example

use monty::MontyRun;
use monty_types::{CompileOptions, MontyObject};

let runner = MontyRun::new(
    "x + 1".to_owned(),
    "test.py",
    vec!["x".to_owned()],
    CompileOptions::default(),
)
.unwrap();
let result = runner.run_no_limits(vec![MontyObject::Int(41)]).unwrap();
assert_eq!(result, MontyObject::Int(42));

new

pub fn new(
    code: String,
    script_name: &str,
    input_names: Vec<String>,
    options: CompileOptions,
) -> Result<Self, MontyException>

Creates a new run snapshot by parsing the given code.

This only parses and prepares the code - no heap or namespaces are created yet. Call run or start with inputs to execute it.

Arguments

  • code - The Python code to execute
  • script_name - The script name for error messages; its final path component is what __file__ places under the working directory (/main.py for main.py or src/main.py at the root)
  • input_names - Names of input variables
  • options - CompileOptions controlling CPython divergences; usually CompileOptions::default()

Errors

Returns MontyException if the code cannot be parsed.

code

pub fn code(&self) -> &str

Returns the code that was parsed to create this snapshot.

with_host_clock

pub fn with_host_clock(self, clock: HostClock) -> Self

Chooses what date.today() and datetime.now() read, replacing the System clock a runner starts with.

Only run and run_no_limits consult it: they have no host to suspend to. Under start the host answers both calls itself, so a clock set here is ignored.

Denied takes the clock away; Fixed freezes an instant, for runs that have to be reproducible. Reading the wall clock is a weak but real capability — see docs/security.md.

use monty::MontyRun;
use monty_types::{CompileOptions, HostClock, MontyObject};

let code = "from datetime import date\ndate.today().year".to_owned();
let clock = HostClock::Fixed { unix_seconds: 1_700_000_000, microsecond: 0, local_offset_seconds: 0 };
let runner = MontyRun::new(code, "today.py", vec![], CompileOptions::default()).unwrap().with_host_clock(clock);
assert_eq!(runner.run_no_limits(vec![]).unwrap(), MontyObject::Int(2023));

set_cwd

pub fn set_cwd(&mut self, cwd: &str)

Sets the sandbox working directory the run starts in (default /).

cwd is an absolute POSIX virtual path, passed through normalize_virtual_path so os.getcwd() reports a canonical directory: it is what relative paths in open() / os / pathlib calls resolve against before reaching the host. Hosts typically pass the first mount’s virtual path.

run

pub fn run(
    &self,
    inputs: Vec<MontyObject>,
    resource_tracker: ResourceTracker,
    print: PrintWriter<'_>,
) -> Result<MontyObject, MontyException>

Executes the code to completion assuming not external functions or snapshotting.

This is marginally faster than running with snapshotting enabled since we don’t need to track the position in code, but does not allow calling of external functions.

Arguments

  • inputs - Values to fill the first N slots of the namespace
  • resource_tracker - Custom resource tracker implementation
  • print - print output writer

run_no_limits

pub fn run_no_limits(
    &self,
    inputs: Vec<MontyObject>,
) -> Result<MontyObject, MontyException>

Executes the code to completion with no resource limits specified (will use the default), printing to stdout/stderr.

start

pub fn start(
    self,
    inputs: Vec<MontyObject>,
    resource_tracker: ResourceTracker,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Starts execution with the given inputs and resource tracker, consuming self.

Creates the heap and namespaces, then begins execution.

For iterative execution, start consumes self and returns a RunProgress:

This enables snapshotting execution state and returning control to the host application during long-running computations.

Arguments

  • inputs - Initial input values (must match length of input_names from new)
  • resource_tracker - Resource tracker for the execution
  • print - Writer for print output

Errors

Returns MontyException if:

  • The number of inputs doesn’t match the expected count
  • An input value is invalid (e.g., MontyObject::Repr)
  • A runtime error occurs during execution

Panics

This method should not panic under normal operation. Internal assertions may panic if the VM reaches an inconsistent state (indicating a bug).

Implements: Clone, Debug, Deserialize<'de>, Serialize.

RunProgress

pub enum RunProgress {
    /// Execution paused at an external function call, or a method call on a
    /// host object (`object_id` set).
    FunctionCall(FunctionCall),
    /// Execution paused for an OS-level operation (filesystem, network, etc.).
    OsCall(OsCall),
    /// All async tasks are blocked waiting for external futures to resolve.
    ResolveFutures(ResolveFutures),
    /// Execution paused for an unresolved name lookup.
    NameLookup(NameLookup),
    /// Execution completed with a final result.
    Complete(monty_types::MontyObject),
}

Result of a single step of iterative execution.

Each variant wraps a dedicated struct that owns the execution state and exposes only the resume methods relevant to that suspension reason.

into_function_call

pub fn into_function_call(self) -> Option<FunctionCall>

Consumes the progress and returns the FunctionCall struct if this is a function call.

into_os_call

pub fn into_os_call(self) -> Option<OsCall>

Consumes the progress and returns the OsCall struct if this is an OS call.

into_complete

pub fn into_complete(self) -> Option<MontyObject>

Consumes the progress and returns the final value if execution completed.

into_resolve_futures

pub fn into_resolve_futures(self) -> Option<ResolveFutures>

Consumes the progress and returns the ResolveFutures struct.

into_name_lookup

pub fn into_name_lookup(self) -> Option<NameLookup>

Consumes the progress and returns the NameLookup struct.

Implements: Debug, Deserialize<'de>, Serialize.

FunctionCall

pub struct FunctionCall {
    /// The name of the function or method being called.
    pub function_name: String,
    /// The positional arguments passed to the function.
    pub args: Vec<monty_types::MontyObject>,
    /// The keyword arguments passed to the function (key, value pairs).
    pub kwargs: Vec<(monty_types::MontyObject, monty_types::MontyObject)>,
    /// Unique identifier for this call (used for async correlation).
    pub call_id: u32,
    /// Uuid of the routed receiver — an instance, or a class type (a
    /// classmethod call, or construction spelled `__call__`); `None` for
    /// plain external function calls.
    pub object_id: Option<monty_types::MontyUuid>,
    /// The host may await a coroutine and answer with `Self::resume_eager`.
    pub allow_eager_await: bool,
    /* private fields */
}

Execution paused at an external function call or dataclass method call.

The host can choose how to handle this:

  • Sync resolution: Call resume to push the result and continue.
  • Async resolution: Call resume_pending to push an ExternalFuture and continue.

When using async resolution, the code continues and may await the future later. If the future isn’t resolved when awaited, execution yields with ResolveFutures.

When object_id is set, this represents a method call on a host-backed object (construction of a host class is a __call__ method call): route it to the host object with that uuid — the receiver is NOT in args.

tracker_mut

pub fn tracker_mut(&mut self) -> &mut ResourceTracker

Returns a mutable reference to the resource tracker.

This allows modifying resource limits between execution phases, e.g. setting a time limit before resuming after an external function call.

tracker

pub fn tracker(&self) -> &ResourceTracker

Returns the resource tracker while execution is suspended.

resume

pub fn resume(
    self,
    result: impl Into<ExtFunctionResult>,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Resumes execution with the return value or exception from the external function.

Consumes self and returns the next execution progress.

Arguments

  • result — The return value, exception, or pending future marker.
  • print — Writer for print() output.

resume_pending

pub fn resume_pending(self, print: PrintWriter<'_>) -> Result<RunProgress, MontyException>

Resumes execution by pushing an ExternalFuture instead of a concrete value.

This is the async resolution pattern: the host continues execution with a pending future. The code can then await this future later. If the code awaits the future before it’s resolved, execution will yield with RunProgress::ResolveFutures.

Uses self.call_id internally — no need to pass it again.

Arguments

  • print — Writer for print output.

resume_eager

pub fn resume_eager(
    self,
    result: Result<MontyObject, MontyException>,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Resumes with a settled coroutine, preserving its awaitable value and exception timing. Only use when Self::allow_eager_await is true; synchronous returns use Self::resume.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Aborts the feed with an uncatchable exception; see OsCall::abort.

Implements: Debug, Deserialize<'de>, Serialize.

NameLookup

pub struct NameLookup {
    /// The name being looked up.
    pub name: String,
    /* private fields */
}

Execution paused for an unresolved name lookup, or — when object_id is set — a lazy attribute lookup on a host-backed object.

The host should check if the name corresponds to a known external function, value, or instance attribute. Call resume with NameLookupResult::Value to continue, NameLookupResult::Undefined to raise NameError (plain lookups) / AttributeError (instance lookups), or NameLookupResult::Error to raise a host exception in the sandbox.

The namespace slot and scope are managed internally — the host only needs to provide the name resolution result.

object_id

pub fn object_id(&self) -> Option<MontyUuid>

Host identity of the receiver for a lazy attribute lookup; None for a plain global/local name lookup.

tracker

pub fn tracker(&self) -> &ResourceTracker

Returns the resource tracker while execution is suspended.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Aborts the feed with an uncatchable exception; see OsCall::abort.

resume

pub fn resume(
    self,
    result: impl Into<NameLookupResult>,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Resumes execution after name resolution.

For a plain lookup, caches the resolved value in the appropriate slot (globals or stack) before pushing it, and Undefined raises NameError. For an instance attribute lookup, the value is pushed as the attribute expression’s result (never cached), and Undefined raises AttributeError. Error raises the host’s exception in the sandbox, bypassing any hasattr() / getattr() default.

Arguments

  • result — The resolved value, Undefined, or a host exception.
  • print — Writer for print output.

Implements: Debug, Deserialize<'de>, Serialize.

OsCall

pub struct OsCall {
    /// Typed OS-call dispatch value (variant + args).
    pub function_call: monty_types::OsFunctionCall,
    /// Unique identifier for this call (used for async correlation).
    pub call_id: u32,
    /* private fields */
}

Execution paused for an OS-level operation.

The host should execute the OS operation (filesystem, network, etc.) and call resume(return_value, print) to provide the result and continue.

This enables sandboxed execution where the interpreter never directly performs I/O.

function_call is a tagged OsFunctionCall whose variants carry the typed args directly. Host bindings that need a generic (positional, keyword) MontyObject view can call OsFunctionCall::to_args (the public projection method).

resume

pub fn resume(
    self,
    result: impl Into<ExtFunctionResult>,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Resumes execution with the OS call result.

Arguments

  • result — The return value or exception from the OS operation.
  • print — Writer for print() output.

resume_with

pub fn resume_with(
    self,
    print: PrintWriter<'_>,
    handler: impl FnOnce(OsFunctionCall) -> ExtFunctionResult,
) -> Result<RunProgress, MontyException>

Dispatches the call to handler and resumes execution with its result.

handler receives the OsFunctionCall by value, so large WriteText / WriteBytes payloads move into the host without cloning. Prefer this over reading Self::function_call and calling Self::resume separately when the handler consumes the call.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Ends the feed by raising exc uncatchably at the suspended call.

The exception builds a traceback but bypasses sandbox handlers. Pending file effects roll back. Always returns Err.

tracker

pub fn tracker(&self) -> &ResourceTracker

Returns the resource tracker while execution is suspended.

Implements: Debug, Deserialize<'de>, Serialize.

ResolveFutures

pub struct ResolveFutures { /* private fields */ }

Execution state paused while waiting for external future results.

Supports incremental resolution — you can provide partial results and Monty will continue running until all tasks are blocked again.

Use pending_call_ids to see which calls are pending, then call resume with some or all of the results.

pending_call_ids

pub fn pending_call_ids(&self) -> &[u32]

Returns unresolved call IDs for this suspended state.

tracker

pub fn tracker(&self) -> &ResourceTracker

Returns the resource tracker while execution is suspended.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Aborts with an uncatchable exception and abandons pending futures.

resume

pub fn resume(
    self,
    results: Vec<(u32, ExtFunctionResult)>,
    print: PrintWriter<'_>,
) -> Result<RunProgress, MontyException>

Resumes execution with results for some or all pending futures.

Incremental resolution: You don’t need to provide all results at once. If you provide a partial list, Monty will:

  1. Mark those futures as resolved
  2. Unblock any tasks waiting on those futures
  3. Continue running until all tasks are blocked again
  4. Return ResolveFutures with the remaining pending calls

Arguments

  • results — List of (call_id, result) pairs. Can be a subset of pending calls.
  • print — Writer for print output.

Errors

Returns MontyException if any call_id in results is not in the pending set.

Implements: Debug, Deserialize<'de>, Serialize.

MontyRepl

pub struct MontyRepl { /* private fields */ }

Stateful REPL session that executes snippets incrementally without replay.

MontyRepl preserves heap and global variable state between snippets. Each feed_run or feed_start call compiles and executes only the new snippet against the current state, avoiding the cost and semantic risks of replaying prior code.

new

pub fn new(
    script_name: &str,
    resource_tracker: ResourceTracker,
    options: CompileOptions,
) -> Self

Creates an empty REPL session with no code parsed or executed.

All code execution is driven through feed_run or feed_start. This separates construction from execution, matching the pattern used by MontyRun::new. The CompileOptions apply to every snippet fed to the session.

with_host_clock

pub fn with_host_clock(self, clock: HostClock) -> Self

Chooses what date.today() and datetime.now() read, replacing the System clock a session starts with.

Only the non-suspending feed_run and call_function consult it. Under feed_start the host answers both calls itself, so a clock set here is ignored.

set_cwd

pub fn set_cwd(&mut self, cwd: &str)

Switches the sandbox working directory (initially /).

cwd is an absolute POSIX virtual path, normalized here like MontyRun::set_cwd: os.getcwd() reports it and relative paths in open() / os / pathlib calls resolve against it before reaching the host. The directory then persists across snippets, including a snippet’s os.chdir, until the host switches it again.

tracker

pub fn tracker(&self) -> &ResourceTracker

Returns the resource tracker that will be used for the next snippet.

This is primarily intended for host integrations that need to attach per-execution state, such as cancellation markers, to an existing REPL.

tracker_mut

pub fn tracker_mut(&mut self) -> &mut ResourceTracker

Returns mutable access to the resource tracker for the next snippet.

REPL hosts use this to install ephemeral execution controls, such as async cancellation flags, before calling feed_start.

feed_start

pub fn feed_start(
    self,
    code: &str,
    inputs: Vec<(String, MontyObject)>,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Starts executing a new snippet and returns suspendable REPL progress.

This is the REPL equivalent of MontyRun::start: execution may complete, suspend at external calls / OS calls / unresolved futures, or raise a Python exception. Resume with the returned state object and eventually recover the updated REPL from ReplProgress::into_complete.

Unlike MontyRepl::feed_run, this method consumes self so runtime state can be safely moved into snapshot objects for serialization and cross-process resume.

On a Python-level runtime exception the REPL is not destroyed: it is returned inside ReplStartError so the caller can continue feeding subsequent snippets against the same heap and namespace state.

Errors

Returns a boxed ReplStartError for syntax, compile-time, or runtime failures — the REPL session is always preserved inside the error.

feed_run

pub fn feed_run(
    &mut self,
    code: &str,
    inputs: Vec<(String, MontyObject)>,
    print: PrintWriter<'_>,
) -> Result<MontyObject, MontyException>

Feeds and executes a new snippet against the current REPL state to completion.

This compiles only code using the existing global slot map, extends the global namespace if new names are introduced, and executes the snippet once. Previously executed snippets are never replayed. If execution raises after partially mutating globals, those mutations remain visible in later feeds, matching Python REPL semantics.

Errors

Returns MontyException for syntax/compile/runtime failures.

call_function

pub fn call_function(
    &mut self,
    name: &str,
    args: Vec<MontyObject>,
    print: PrintWriter<'_>,
) -> Result<MontyObject, MontyException>

Calls a Python function defined in the session by name.

Looks up the function, then executes a synthetic <python-input-N> call expression so failures include a visible host call site.

Errors

Returns MontyException if the function is not found, not callable, raises an exception, or encounters an external function call.

function_names

pub fn function_names(&self) -> Vec<&str>

Returns a list of all callable function names defined in the session.

Includes functions, closures, and functions with default arguments. Does not include builtins or external functions.

has_function

pub fn has_function(&self, name: &str) -> bool

Returns whether a function with the given name exists in the session.

Implements: Debug, Deserialize<'de>, Drop, Serialize.

ReplProgress

pub enum ReplProgress {
    /// Execution paused at an external function call or dataclass method call.
    FunctionCall(ReplFunctionCall),
    /// Execution paused for an OS-level operation.
    OsCall(ReplOsCall),
    /// All async tasks are blocked waiting for external futures to resolve.
    ResolveFutures(ReplResolveFutures),
    /// Execution paused for an unresolved name lookup.
    NameLookup(ReplNameLookup),
    /// Snippet execution completed with the updated REPL and result value.
    Complete { repl: MontyRepl, value: monty_types::MontyObject },
}

Result of a single suspendable REPL snippet execution.

This mirrors RunProgress but returns the updated MontyRepl on completion so callers can continue feeding additional snippets without replaying prior code. Each variant (except Complete) wraps a dedicated struct with only the relevant resume methods.

into_function_call

pub fn into_function_call(self) -> Option<ReplFunctionCall>

Consumes the progress and returns the ReplFunctionCall struct.

into_resolve_futures

pub fn into_resolve_futures(self) -> Option<ReplResolveFutures>

Consumes the progress and returns the ReplResolveFutures struct.

into_name_lookup

pub fn into_name_lookup(self) -> Option<ReplNameLookup>

Consumes the progress and returns the ReplNameLookup struct.

into_complete

pub fn into_complete(self) -> Option<(MontyRepl, MontyObject)>

Consumes the progress and returns the completed REPL and value.

into_repl

pub fn into_repl(self) -> MontyRepl

Extracts the REPL session from any progress variant, discarding the in-flight execution state.

Use this to recover the REPL when you need to abandon the current snippet (e.g. because feed_run doesn’t support async futures). The REPL state reflects any mutations that occurred before the snapshot was taken.

tracker

pub fn tracker(&self) -> &ResourceTracker

Returns the session’s resource tracker, whatever the progress state.

Lets hosts read resource accounting — e.g. cumulative execution time for max_duration budgeting — at any suspension point without consuming the progress.

Implements: Debug, Deserialize<'de>, Serialize.

ReplFunctionCall

pub struct ReplFunctionCall {
    /// The name of the function or method being called.
    pub function_name: String,
    /// The positional arguments passed to the function.
    pub args: Vec<monty_types::MontyObject>,
    /// The keyword arguments passed to the function (key, value pairs).
    pub kwargs: Vec<(monty_types::MontyObject, monty_types::MontyObject)>,
    /// Unique identifier for this call (used for async correlation).
    pub call_id: u32,
    /// Uuid of the routed receiver — an instance, or a class type (a
    /// classmethod call, or construction spelled `__call__`); `None` for
    /// plain external function calls. The receiver is NOT included in `args`.
    pub object_id: Option<monty_types::MontyUuid>,
    /// The host may await a coroutine and answer with `Self::resume_eager`.
    pub allow_eager_await: bool,
    /* private fields */
}

REPL execution paused at an external function call or host-class method call.

Resume with resume to provide the return value and continue, or resume_pending to push an ExternalFuture for async resolution.

into_repl

pub fn into_repl(self) -> MontyRepl

Extracts the REPL session, discarding the in-flight execution state.

Restores globals from the VM snapshot so the REPL remains usable.

resume

pub fn resume(
    self,
    result: impl Into<ExtFunctionResult>,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Resumes snippet execution with an external result.

resume_pending

pub fn resume_pending(
    self,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Resumes execution by pushing an ExternalFuture for async resolution.

Uses self.call_id internally — no need to pass it again.

resume_eager

pub fn resume_eager(
    self,
    result: Result<MontyObject, MontyException>,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Resumes with a settled coroutine, preserving its awaitable value and exception timing. Only use when Self::allow_eager_await is true; synchronous returns use Self::resume.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Aborts the snippet with an uncatchable exception; see ReplOsCall::abort.

Implements: Debug, Deserialize<'de>, Serialize.

ReplNameLookup

pub struct ReplNameLookup {
    /// The name being looked up.
    pub name: String,
    /* private fields */
}

REPL execution paused for an unresolved name lookup, or — when object_id is set — a lazy attribute lookup on a host-backed object (a class instance or class type).

The host should check if the name corresponds to a known external function, value, or instance attribute. Call resume with the appropriate NameLookupResult. The namespace slot and scope are managed internally.

into_repl

pub fn into_repl(self) -> MontyRepl

Extracts the REPL session, discarding the in-flight execution state.

Restores globals from the VM snapshot so the REPL remains usable.

object_id

pub fn object_id(&self) -> Option<MontyUuid>

Identity of the receiver for a lazy attribute lookup; None for a plain global/local name lookup.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Aborts the snippet with an uncatchable exception; see ReplOsCall::abort.

resume

pub fn resume(
    self,
    result: NameLookupResult,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Resumes execution after name resolution.

For a plain lookup, caches the resolved value in the namespace slot and Undefined raises NameError; for a host attribute lookup (instance or class type) the value is pushed uncached and Undefined raises AttributeError. Error raises the host’s exception in the sandbox, bypassing any hasattr() / getattr() default.

Implements: Debug, Deserialize<'de>, Serialize.

ReplOsCall

pub struct ReplOsCall {
    /// Typed OS-call dispatch value (variant + args).
    pub function_call: monty_types::OsFunctionCall,
    /// Unique identifier for this call (used for async correlation).
    pub call_id: u32,
    /* private fields */
}

REPL execution paused for an OS-level operation.

Resume with resume(result, print) to provide the OS call result and continue.

into_repl

pub fn into_repl(self) -> MontyRepl

Extracts the REPL session, discarding the in-flight execution state.

Restores globals from the VM snapshot so the REPL remains usable.

resume

pub fn resume(
    self,
    result: impl Into<ExtFunctionResult>,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Resumes snippet execution with the OS call result.

resume_with

pub fn resume_with(
    self,
    print: PrintWriter<'_>,
    handler: impl FnOnce(OsFunctionCall) -> ExtFunctionResult,
) -> Result<ReplProgress, Box<ReplStartError>>

REPL mirror of OsCall::resume_with — dispatches the call to handler (which receives the OsFunctionCall by value, so write payloads move without cloning) and resumes with its result.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Raises exc uncatchably at the suspended call.

Always returns Err with a reusable session; see OsCall::abort.

Implements: Debug, Deserialize<'de>, Serialize.

ReplResolveFutures

pub struct ReplResolveFutures { /* private fields */ }

REPL execution state blocked on unresolved external futures.

This is the REPL-aware counterpart to ResolveFutures.

into_repl

pub fn into_repl(self) -> MontyRepl

Extracts the REPL session, restoring globals from the suspended VM state.

As with the other REPL snapshot types, globals live inside the VM snapshot while execution is suspended. Recovering the REPL for a cancelled or abandoned async snippet must put those globals back so previously defined REPL bindings remain available, and releases the suspended tasks and stack so nothing leaks into the session heap. The compiler tables are committed too: the abandoned snippet may have rebound a global to a function or literal only they can resolve.

pending_call_ids

pub fn pending_call_ids(&self) -> &[u32]

Returns unresolved call IDs for this suspended state.

abort

pub fn abort(
    self,
    exc: MontyException,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Aborts with an uncatchable exception and abandons pending futures.

resume

pub fn resume(
    self,
    results: Vec<(u32, ExtFunctionResult)>,
    print: PrintWriter<'_>,
) -> Result<ReplProgress, Box<ReplStartError>>

Resumes snippet execution with zero or more resolved futures.

Supports incremental resolution: callers can provide only a subset of pending call IDs and continue resolving over multiple resumes.

All errors — including API misuse (unknown call_id) and Python-level runtime failures — are returned as a boxed ReplStartError so the REPL session is always preserved.

Implements: Debug, Deserialize<'de>, Serialize.

ReplStartError

pub struct ReplStartError {
    /// REPL session state after the failed snippet — ready for further use.
    pub repl: MontyRepl,
    /// The Python exception that was raised.
    pub error: monty_types::MontyException,
}

Error returned when a REPL snippet raises a Python exception during feed_start or a resume().

Unlike syntax/compile errors which consume the REPL, runtime errors preserve the full session state so the caller can inspect the error and continue feeding subsequent snippets. Any global mutations that occurred before the exception remain visible in the returned repl.

Implements: Debug.

ReplContinuationMode

pub enum ReplContinuationMode {
    /// The current snippet is syntactically complete and can run now.
    Complete,
    /// The snippet is incomplete and needs more continuation lines.
    IncompleteImplicit,
    /// The snippet opened an indented block and should wait for a trailing blank
    /// line before execution, matching CPython interactive behavior.
    IncompleteBlock,
}

Parse-derived continuation state for interactive REPL input collection.

monty-runtime uses this to decide whether to execute the buffered snippet immediately, keep collecting continuation lines, or require a terminating blank line for block statements (if:, def:, etc.).

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq.

detect_repl_continuation_mode

pub fn detect_repl_continuation_mode(source: &str) -> ReplContinuationMode;

Detects whether REPL source is complete or needs more input.

This mirrors CPython’s broad interactive behavior:

  • Incomplete bracketed / parenthesized / triple-quoted constructs continue.
  • Decorators continue until their class or function definition arrives.
  • Clause headers (if:, def:, etc.) require an indented body and then a terminating blank line before execution.
  • All other parse outcomes are treated as complete (either valid code or a syntax error that should be shown immediately).

Session

pub enum Session {
    /// Between feeds, ready for the next snippet.
    Idle(Box<MontyRepl>),
    /// Mid-feed, waiting on a resume.
    Suspended(Box<ReplProgress>),
    /// A one-shot `MontyRun` execution paused at a suspension. Not a
    /// repl, so it cannot be fed further — only resumed to completion.
    Running(Box<RunProgress>),
}

Where a dumped session was paused. The variant order is mirrored by SessionRef and encoded as a postcard discriminant — keep them in step.

Both arms are boxed because they differ by hundreds of bytes inline; a Box<T> serializes exactly as T, so this does not change the wire form.

Implements: Debug, Deserialize<'de>.

SessionRef

pub enum SessionRef<'a> {
    /// Between feeds, ready for the next snippet.
    Idle(&'a MontyRepl),
    /// Mid-feed, waiting on a resume.
    Suspended(&'a ReplProgress),
    /// A paused one-shot `MontyRun` execution.
    Running(&'a RunProgress),
}

Borrowed counterpart of Session used when dumping, so a live session can be serialized without moving the repl out of the host’s own state.

Implements: Debug, Serialize.

dump

pub fn dump(
    script_name: &str,
    type_check: Option<&monty_types::TypeCheckState>,
    state: SessionRef<'_>,
) -> Result<Vec<u8>, postcard::Error>;

Serializes a live session and its metadata into a versioned dump, readable by Dump::load.

Takes the state by reference because dumping is read-only: the caller keeps its session and can carry on feeding it.

Errors

Returns an error if serialization fails.

Dump

pub struct Dump {
    /// Script name used for tracebacks and type-check diagnostics.
    pub script_name: String,
    /// `Some` when the session was created with type checking enabled.
    pub type_check: Option<monty_types::TypeCheckState>,
    /// The interpreter state, and where it was paused.
    pub state: Session,
}

A complete REPL session snapshot: the interpreter state plus the session-scoped context that lives outside it.

The metadata travels with the state because a restored session is otherwise silently downgraded — losing script_name corrupts tracebacks, and losing type_check disables enforcement the parent asked for.

load

pub fn load(bytes: &[u8]) -> Result<Self, DumpError>

Restores a session dumped by dump.

Errors

Returns DumpError for a dump this build cannot read — most usefully DumpError::VersionMismatch, which names both versions so a host can tell a stale snapshot from a corrupt one.

Implements: Debug, Deserialize<'de>.

DumpError

pub enum DumpError {
    /// Too short to hold a header, or missing the magic prefix.
    NotADump,
    /// Written by a build using a different dump format version.
    VersionMismatch { found: u16, expected: u16 },
    /// Header was valid but the postcard payload did not decode.
    Payload(postcard::Error),
}

Why a dump could not be restored.

Distinguishes the three failures a host cares about, because they need different responses: an old snapshot should be discarded and rebuilt, while a payload error on a current-version dump means corruption.

Implements: Debug, Display, Eq, Error, PartialEq, StructuralPartialEq.

DUMP_VERSION

pub const DUMP_VERSION: u16 = 10;

Version of the dump’s postcard schema.

Bump this whenever a serialized discriminant can shift, so older dumps are rejected instead of decoding as their neighbour. That covers the interpreter’s own types and everything reachable from Dump — notably TypeCheckingConfig in monty-types.

defer_drop

macro_rules! defer_drop { /* macro body */ }

The preferred way to ensure a DropWithContext value is cleaned up on every code path.

Creates a DropGuard and immediately rebinds $value as &V and $ctx as &mut C via DropGuard::as_parts. The original owned value is moved into the guard, which will call DropWithContext::drop_with when scope exits — whether that’s normal completion, early return via ?, continue, or any other branch.

Beyond safety, this is often much more concise than inserting drop_with calls in every branch of complex control flow. For mutable access to the value, use defer_drop_mut!.

Limitation

The macro rebinds $ctx as a new let binding, so it cannot be used when $ctx is self. In &mut self methods, first assign let this = self; and pass this.

defer_drop_mut

macro_rules! defer_drop_mut { /* macro body */ }

Like defer_drop!, but rebinds $value as &mut V via DropGuard::as_parts_mut.

Use this when the value needs to be mutated in place — for example, advancing an iterator with for_next(), or swapping values during a min/max comparison.