Skip to content

monty-types

The shared boundary types exchanged between hosts and the sandbox: values, exceptions, resource limits and host-call payloads. Host-side code depends on this crate rather than on the interpreter.

args

pub mod args;

ToArgs / ToMontyObject — projection of typed args structs into the (positional, keyword) MontyObject pairs host callbacks consume. The #[derive(ToArgs)] macro in monty-macros emits impls of these traits via crate::args::… paths, which resolve in this crate.

ToArgs

pub trait ToArgs {
    fn to_args(self) -> (Vec<MontyObject>, Vec<(MontyObject, MontyObject)>);
}

Projects a typed args struct into the (positional, keyword) MontyObject pair host callbacks expect. Consumes self to avoid cloning owned fields.

Inverse of monty’s internal FromArgs (ArgValues → struct); ToArgs is struct → host-facing (args, kwargs). Driven by os::OsFunctionCall::to_args for the monty-python / monty-js bindings.

ToMontyObject

pub trait ToMontyObject {
    fn into_monty_object(self) -> MontyObject;
}

Consume self into a MontyObject.

MontyObject is the host-facing, heap-free representation. Implementers just shape themselves into the most natural MontyObject variant — StringMontyObject::String, Vec<u8>MontyObject::Bytes, etc.

format

pub mod format;

Pure CPython-compatible formatting helpers shared by the boundary types: string/bytes repr() escaping, shortest-round-trip float rendering, and timezone-offset timedelta reprs.

string_repr_fmt

pub fn string_repr_fmt(s: &str, f: &mut impl Write) -> fmt::Result;

Writes a Python repr() string for a given string slice to a formatter.

Quote choice matches CPython: single quotes by default, switching to double quotes only when the string contains a ' but no " (so the quote needn’t be escaped). Backslash, the active quote, and \n/\t/\r use the short escapes; any other non-printable character is escaped numerically (\xNN/\uNNNN/\UNNNNNNNN), e.g. repr('\x00') == "'\\x00'" and repr('\xa0') == "'\\xa0'".

“Non-printable” matches CPython’s str.isprintable (see repr_needs_escape): Unicode categories C* and Z*, except the ASCII space. Category data comes from unicode-general-category, whose Unicode version may differ slightly from CPython’s, affecting only recently (re)assigned code points.

StringRepr

pub struct StringRepr<'a>(pub &'a str);

Formatter for a Python repr() string.

Implements: Debug, Display.

bytes_repr_fmt

pub fn bytes_repr_fmt(bytes: &[u8], f: &mut impl Write) -> fmt::Result;

Writes a CPython-compatible repr string for bytes to a formatter.

Format: b'...' or b"..." depending on content.

  • Uses single quotes by default
  • Switches to double quotes if bytes contain ' but not "
  • Escapes: \\, \t, \n, \r, \xNN for non-printable bytes

bytes_repr

pub fn bytes_repr(bytes: &[u8]) -> String;

Returns a CPython-compatible repr string for bytes.

Convenience wrapper around bytes_repr_fmt that returns an owned String.

FormatFloat

pub struct FormatFloat(pub f64);

A Display adapter that writes a float exactly as CPython’s repr()/str() (identical for floats in Python 3): the shortest decimal string that round-trips, switching to scientific notation when the base-10 exponent is < -4 or >= 16, and always keeping at least one fractional digit (1.0, never 1) — 1e16"1e+16", 1234.5"1234.5", inf/nan lowercased.

This is the default rendering for a bare f"{x}", str(x), repr(x) and floats inside container reprs — not the format mini-language (that’s format_float_g et al, in monty). Rust can’t do this directly: its f64 Display never uses scientific notation (1e16 prints as 10000000000000000) and renders NaN as "NaN".

As a Display adapter it writes straight to the caller’s sink with no heap allocation: it borrows Rust’s shortest-digits guarantee via {:e} into a small stack buffer (an f64 {:e} is ASCII and ≤ 24 bytes) and re-lays-out those digits per CPython’s rules.

Implements: Display.

utf8_error_reason

pub fn utf8_error_reason(first_bad_byte: u8, error_len: Option<usize>) -> &'static str;

Classifies an invalid-UTF-8 error into CPython’s reason wording, from the first unexpected byte and Utf8Error::error_len().

error_len == None means the input ended mid-sequence (unexpected end of data); otherwise a byte that is a legal multi-byte lead (0xC2–0xF4) was followed by an invalid continuation, and anything else (stray continuation bytes, the overlong leads 0xC0/0xC1, 0xF5–0xFF) is an invalid start byte. Public (re-exported at the crate root) so monty-fs produces identical wording for text-mode file reads.

format_offset_timedelta_repr

pub fn format_offset_timedelta_repr(offset_seconds: i32) -> String;

Formats the canonical datetime.timedelta(...) repr for a fixed timezone offset in seconds, normalized like Python’s timedelta (days may be negative, seconds in 0..86400) — e.g. -18000datetime.timedelta(days=-1, seconds=68400). Used by the datetime.timezone reprs of MontyObject.

MONTY_VERSION

pub const MONTY_VERSION: &str = "0.0.21";

The monty version this build was compiled as.

BuiltinsFunctions

pub enum BuiltinsFunctions {
    Abs,
    All,
    Any,
    Bin,
    Chr,
    Divmod,
    Enumerate,
    Filter,
    Getattr,
    Hasattr,
    Hash,
    Hex,
    Id,
    Isinstance,
    Len,
    Map,
    Max,
    Min,
    Next,
    Oct,
    Open,
    Ord,
    Pow,
    Print,
    Repr,
    Reversed,
    Round,
    Setattr,
    Sorted,
    Sum,
    Type,
    Zip,
    /// `object.__setattr__(obj, name, value)` — the write that bypasses a
    /// class's attribute hooks, reached only through `object`. Its name is not
    /// an identifier, so it can never resolve as a bare global.
    ///
    /// The first variant whose name is not its lowercased identifier, so it
    /// needs both renames: serde and strum each carry the name across a
    /// different boundary (JSON vs. `Display`/`FromStr`) and must agree.
    ObjectSetattr,
}

Enumerates every interpreter-native Python builtin function.

Listed alphabetically per https://docs.python.org/3/library/functions.html Commented-out variants are not yet implemented.

Note: Type constructors are handled by the Type enum, not here.

Uses strum derives for automatic Display, FromStr, and IntoStaticStr implementations. All variants serialize to lowercase (e.g., Print -> “print”).

from_repr

pub const fn from_repr(discriminant: u8) -> Option<BuiltinsFunctions>

Try to create Self from the raw representation

Implements: Clone, Copy, Debug, Deserialize<'de>, Display, Eq, From<&'_derivative_strum BuiltinsFunctions>, From<BuiltinsFunctions>, FromStr, Hash, PartialEq, Serialize, StructuralPartialEq, TryFrom<&str>.

CodeLoc

pub struct CodeLoc {
    /// Line number (1-based).
    pub line: u32,
    /// Column number (1-based), counted in characters (not bytes).
    pub column: u32,
}

A line and column position in source code.

Uses 1-based indexing for both line and column to match Python’s conventions.

u32 matches ruff_text_size::TextSize, which underpins all source ranges returned by the parser, so conversions between the two are zero-cost.

new

pub fn new(line: u32, column: u32) -> Self

Creates a new CodeLoc from 0-based values.

Lines and columns numbers are 1-indexed for display, hence + 1. Saturates at u32::MAX rather than panicking — overflow here is already unreachable for any source ruff will accept (it caps source size at 4 GiB), and saturation keeps the parser panic-free even if that ever changes.

Implements: Clone, Copy, Debug, Default, Deserialize<'de>, Eq, Hash, PartialEq, Serialize, StructuralPartialEq.

ExcData

pub enum ExcData {
    /// No structured payload — every exception type without a variant below.
    None,
    /// `UnicodeDecodeError` / `UnicodeEncodeError` constructor fields.
    /// Boxed to keep the common `None` case (and every exception embedding
    /// this enum) small.
    Unicode(Box<UnicodeErrorData>),
    /// `json.JSONDecodeError` attribute fields. Boxed like
    /// `ExcData::Unicode` to keep the enum small.
    Json(Box<JsonErrorData>),
}

Structured payload attached to exception types whose CPython counterparts carry more than a message. Currently unicode and json decode errors have one; the enum leaves room for future variants (e.g. OSError’s errno/filename) without another field on every exception.

unicode

pub fn unicode(&self) -> Option<&UnicodeErrorData>

The unicode-error fields, if this is ExcData::Unicode.

json

pub fn json(&self) -> Option<&JsonErrorData>

The json-error fields, if this is ExcData::Json.

Implements: Clone, Debug, Default, Deserialize<'de>, Hash, PartialEq, Serialize, StructuralPartialEq.

ExcType

pub enum ExcType {
    /// primary exception class - matches any exception in isinstance checks.
    ///
    /// Also the `Default` — required so `Type` (which embeds an `ExcType` in
    /// its `Exception` variant) can derive `strum::EnumIter`.
    Exception,
    /// System exit exceptions
    BaseException,
    SystemExit,
    KeyboardInterrupt,
    /// Intermediate class for arithmetic errors.
    ArithmeticError,
    /// Subclass of ArithmeticError.
    OverflowError,
    /// Subclass of ArithmeticError.
    ZeroDivisionError,
    /// Intermediate class for lookup errors.
    LookupError,
    /// Subclass of LookupError.
    IndexError,
    /// Subclass of LookupError.
    KeyError,
    /// Intermediate class for runtime errors.
    RuntimeError,
    /// Subclass of RuntimeError.
    NotImplementedError,
    /// Subclass of RuntimeError.
    RecursionError,
    AttributeError,
    /// Subclass of AttributeError (from dataclasses module).
    FrozenInstanceError,
    NameError,
    /// Subclass of NameError - for accessing local variable before assignment.
    UnboundLocalError,
    ValueError,
    /// Subclass of ValueError - for encoding/decoding errors.
    UnicodeDecodeError,
    /// Subclass of ValueError - for encoding errors (e.g. `str.encode('ascii')`
    /// on a string containing non-ASCII characters).
    UnicodeEncodeError,
    /// Subclass of ValueError for invalid JSON syntax in `json.loads()`.
    JsonDecodeError,
    /// Import-related errors (module not found, name not in module).
    ImportError,
    /// Subclass of ImportError - for when a module cannot be found.
    ModuleNotFoundError,
    /// OS-related errors (file not found, permission denied, etc.)
    OSError,
    /// Subclass of OSError - for when a file or directory cannot be found.
    FileNotFoundError,
    /// Subclass of OSError - for when a file already exists.
    FileExistsError,
    /// Subclass of OSError - for when a path is a directory but a file was expected.
    IsADirectoryError,
    /// Subclass of OSError - for when a path is not a directory but one was expected.
    NotADirectoryError,
    /// Subclass of OSError - for when an operation is not permitted (e.g., writing
    /// to a read-only mount, or attempting to access a path outside a mounted directory).
    PermissionError,
    /// `io.UnsupportedOperation` - raised by file objects when a requested
    /// operation isn't allowed by the open mode (e.g. `read()` on `'w'`).
    ///
    /// In CPython this inherits from both `OSError` and `ValueError`. Monty's
    /// `ExcType` enum models single parents, but `Self::is_subclass_of`
    /// matches `UnsupportedOperation` against both `OSError` and `ValueError`
    /// so `except ValueError:` and `except OSError:` both catch it as in
    /// CPython.
    UnsupportedOperation,
    /// Subclass of OSError since Python 3.3 (PEP 3151).
    TimeoutError,
    AssertionError,
    MemoryError,
    StopIteration,
    SyntaxError,
    TypeError,
    /// `re.PatternError` - raised for invalid regex patterns or unsupported regex features.
    ///
    /// # Behavior Note
    ///
    /// Limited to monty's exception type, `PatternError` does not provide `pattern`, `pos`,
    /// `lineno` and `colno` attributes.
    ///
    /// As per CPython's implementation, it would be hard to convert `fancy-regex`'s error
    /// representations into the required attributes.
    RePatternError,
    /// `binascii.Error` - raised by the `base64` codecs for malformed input.
    ///
    /// A `ValueError` subclass in CPython, so `except ValueError:` catches it.
    /// Monty's `binascii` module exposes this class and nothing else.
    BinasciiError,
}

Python exception types supported by the interpreter.

Uses strum derives for automatic Display, FromStr, and Into<&'static str> implementations. The string representation matches the variant name exactly (e.g., ValueError -> “ValueError”), except where a serialize attribute gives a dotted name to disambiguate a stdlib subclass from its builtin parent (binascii.Error from ValueError, say).

ExcType::VARIANTS is the canonical list of those names, and the host bridges mirror it: PYTHON_EXC_NAMES in crates/monty-js/ts/errors.ts and the ExcType literal in pydantic_monty/__init__.py. Both are hand-written, so monty-proto’s exc_type_bridges test reads them and pins them here — adding a variant without wiring the bridges fails that test.

is_subclass_of

pub fn is_subclass_of(self, handler_type: Self) -> bool

Checks if this exception type is a subclass of another exception type.

Implements Python’s exception hierarchy for try/except matching:

  • Exception is the base class for all standard exceptions
  • LookupError is the base for KeyError and IndexError
  • ArithmeticError is the base for ZeroDivisionError and OverflowError
  • RuntimeError is the base for RecursionError and NotImplementedError

Returns true if self would be caught by except handler_type:.

Implements: Clone, Copy, Debug, Default, Deserialize<'de>, Display, Eq, From<&'_derivative_strum ExcType>, From<ExcType>, FromStr, Hash, PartialEq, Serialize, StructuralPartialEq, TryFrom<&str>, VariantNames.

JsonErrorData

pub struct JsonErrorData {
    /// The bare error message, without the `: line N column M (char K)`
    /// suffix the formatted exception message carries.
    pub msg: String,
    /// The document being parsed, matching CPython's `exc.doc`. `None` when
    /// the document exceeds `JsonErrorData::MAX_DOC_LEN` or is not valid
    /// UTF-8 (`json.loads` on `bytes` input).
    pub doc: Option<String>,
    /// Character index of the error in `doc`, matching CPython's `exc.pos`.
    pub pos: usize,
    /// 1-based line of the error, matching CPython's `exc.lineno`.
    pub lineno: usize,
    /// 1-based column of the error, matching CPython's `exc.colno`.
    pub colno: usize,
}

Structured fields of a json.JSONDecodeError, mirroring CPython’s msg / doc / pos / lineno / colno exception attributes.

As with UnicodeErrorData, the payload exists so host bindings can construct a real json.JSONDecodeError instead of falling back to a plain ValueError; sandboxed code never sees these fields. lineno/colno are carried explicitly rather than recomputed from doc because doc may be absent (see JsonErrorData::MAX_DOC_LEN).

MAX_DOC_LEN

pub const MAX_DOC_LEN: usize = _;

Document size cap, mirroring UnicodeErrorData::MAX_OBJECT_LEN: exception payloads are copied into the host once they escape the worker, so doc is dropped (not truncated — a partial document would misplace pos) for larger inputs.

build

pub fn build(msg: &str, doc: &[u8], pos: usize, lineno: usize, colno: usize) -> ExcData

Builds the payload for a decode error on doc, omitting the document when it exceeds Self::MAX_DOC_LEN or is not valid UTF-8.

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

MontyException

pub struct MontyException { /* private fields */ }

Public representation of a Monty exception.

new

pub fn new(exc_type: ExcType, message: Option<String>) -> Self

Create a new MontyException with the given exception type and message.

You can’t provide a traceback here, it’s send when raising the exception.

with_traceback

pub fn with_traceback(
    exc_type: ExcType,
    message: Option<String>,
    traceback: Vec<StackFrame>,
) -> Self

Creates an exception with an explicit traceback.

Most callers should use MontyException::new — the traceback is normally attached when the exception is raised. This constructor exists for boundaries that reconstruct an exception that was raised elsewhere (e.g. deserializing one received from a monty subprocess worker) and must preserve its original frames.

with_data

pub fn with_data(self, data: ExcData) -> Self

Attaches a structured payload — see ExcData. Public for boundaries that reconstruct an exception raised elsewhere (like MontyException::with_traceback); in-process raises attach the payload at the raise site instead.

data

pub fn data(&self) -> &ExcData

The structured payload, ExcData::None for most exceptions.

unicode_data

pub fn unicode_data(&self) -> Option<&UnicodeErrorData>

Structured UnicodeDecodeError/UnicodeEncodeError fields, present only for unicode errors raised by codec operations on objects no larger than UnicodeErrorData::MAX_OBJECT_LEN.

json_data

pub fn json_data(&self) -> Option<&JsonErrorData>

Structured json.JSONDecodeError fields, present only for decode errors raised by json.loads (not for manually raised exceptions).

take_data

pub fn take_data(&mut self) -> ExcData

Removes and returns the structured payload, for consumers (like the Python bindings) that rebuild the native exception and want the payload by value without cloning it.

add_traceback

pub fn add_traceback(&mut self, traceback: impl IntoIterator<Item = StackFrame>)

Appends frames to this exception’s traceback.

runtime_error

pub fn runtime_error(err: impl fmt::Display) -> Self

Shorthand for a traceback-free RuntimeError wrapping err’s display output — used at host boundaries (input conversion, REPL feeds) where no sandbox stack frames exist.

exc_type

pub fn exc_type(&self) -> ExcType

The exception type raised.

message

pub fn message(&self) -> Option<&str>

Optional exception message explaining what went wrong.

Equivalent of python’s exc.args[0]

into_message

pub fn into_message(self) -> Option<String>

Optional exception message explaining what went wrong.

This takes ownership of the MontyException and returns an owned String.

Equivalent of python’s exc.args[0]

traceback

pub fn traceback(&self) -> &[StackFrame]

Stack trace of the exception, first is the outermost frame shown first in the traceback

summary

pub fn summary(&self) -> String

Returns a compact summary of the exception.

Format: ExceptionType: message (e.g., NotImplementedError: feature not supported) If there’s no message, just returns the exception type name.

py_repr

pub fn py_repr(&self) -> String

Returns the exception formatted as Python’s repr() would display it.

Format: ExceptionType('message') (e.g., ValueError('invalid value')) Uses appropriate quoting for messages containing quotes.

Implements: Clone, Debug, Deserialize<'de>, Display, Error, From<MontyException>, PartialEq, Serialize, StructuralPartialEq.

StackFrame

pub struct StackFrame {
    /// The filename where the code is located.
    pub filename: String,
    /// Start position in the source code.
    pub start: CodeLoc,
    /// End position in the source code.
    pub end: CodeLoc,
    /// The name of the frame (function name, or None for module-level code).
    pub frame_name: Option<String>,
    /// The source code line for preview in the traceback.
    ///
    /// Stored as `Arc<str>` rather than `String` so that consecutive frames
    /// referencing the same source line — typical of recursion and tight
    /// helper-function loops — share a single allocation. Without sharing, a
    /// 1000-deep recursive call into code on a long line would clone the
    /// entire line into each frame and amplify memory usage by the call
    /// depth. Serialization roundtrips lose the sharing (each frame gets
    /// its own `Arc`), but that is bounded by the wire size of the
    /// traceback so does not regress the amplification.
    pub preview_line: Option<std::sync::Arc<str>>,
    /// Whether to hide the caret marker in the traceback for this frame.
    ///
    /// Set to `true` for:
    /// - `raise` statements (CPython doesn't show carets for raise)
    /// - `AttributeError` on attribute access (CPython doesn't show carets for these)
    pub hide_caret: bool,
    /// Whether to hide the `, in <name>` part of the frame line.
    ///
    /// Set to `true` for `SyntaxError` where CPython doesn't show the frame name.
    /// CPython's SyntaxError format: `  File "...", line N`
    /// vs runtime error format: `  File "...", line N, in <module>`
    pub hide_frame_name: bool,
}

A single frame in a Python traceback.

Contains all the information needed to display a traceback line: the file location, function name, and optional source code preview.

Caret Markers

Monty uses only ~ characters for caret markers in tracebacks, unlike CPython 3.11+ which uses ~ for the function name and ^ for arguments (e.g., ~~~~~~~~~~~^^^^^^^^^^^). This simplification is intentional - Monty marks the entire expression span uniformly.

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

UnicodeErrorData

pub struct UnicodeErrorData {
    /// The codec name as CPython reports it, e.g. `"utf-8"`, `"ascii"`.
    pub encoding: String,
    /// The full input that failed to encode/decode (`str` for encode errors,
    /// `bytes` for decode errors), matching CPython's `exc.object`.
    pub object: UnicodeErrorObject,
    /// Start of the failing range: a character index for encode errors, a
    /// byte offset for decode errors.
    pub start: usize,
    /// Exclusive end of the failing range, in the same units as `start`.
    pub end: usize,
    /// CPython's reason wording, e.g. `"ordinal not in range(128)"`.
    pub reason: String,
}

Structured fields of a UnicodeDecodeError / UnicodeEncodeError, mirroring CPython’s encoding / object / start / end / reason exception attributes.

Monty exceptions are otherwise message-only; unicode errors additionally carry these fields so host bindings (e.g. pydantic_monty) can construct real UnicodeDecodeError / UnicodeEncodeError instances instead of falling back to a plain ValueError. The payload is omitted when the offending object is larger than UnicodeErrorData::MAX_OBJECT_LEN — exceptions can be stored and copied outside the sandbox’s resource tracker, so an unbounded payload would let huge inputs evade memory limits. Sandboxed code never sees these fields (in-sandbox exceptions expose only args).

MAX_OBJECT_LEN

pub const MAX_OBJECT_LEN: usize = _;

Payload size cap: unicode errors on objects larger than this carry no structured data (hosts fall back to the message-only ValueError). Exception payloads are copied into the host once they escape the worker, so the cap bounds how much host memory a single raise can pin.

encode

pub fn encode(
    encoding: &str,
    object: &str,
    start: usize,
    end: usize,
    reason: &str,
) -> ExcData

Builds the payload for an encode error on object, or ExcData::None when object exceeds Self::MAX_OBJECT_LEN.

decode

pub fn decode(
    encoding: &str,
    object: &[u8],
    start: usize,
    end: usize,
    reason: &str,
) -> ExcData

Builds the payload for a decode error on object, or ExcData::None when object exceeds Self::MAX_OBJECT_LEN. Public so monty-fs can build the payload for text-mode file reads.

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

UnicodeErrorObject

pub enum UnicodeErrorObject {
    /// A decode error's input `bytes`.
    Bytes(Vec<u8>),
    /// An encode error's input `str`.
    Str(String),
}

The object attribute of a unicode error: the input being converted.

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

unicode_decode_error_msg

pub fn unicode_decode_error_msg(
    codec: &str,
    first_byte: u8,
    start: usize,
    end: usize,
    reason: &str,
) -> String;

Formats the message for a UnicodeDecodeError covering the byte range start..end: CPython’s single-byte form (byte 0x\{first_byte:02x\} in position \{start\}) when the range is one byte, otherwise the range form (bytes in position {start}-{end - 1}).

A free function (rather than folded into ExcType::unicode_decode_error), public and re-exported at the crate root, so monty-fs can produce the identical wording when converting a MountError::InvalidUtf8 from a text-mode file read into an exception.

FileMode

pub enum FileMode {
    /// `r` / `rb`: read-only; the file must already exist.
    Read(bool),
    /// `r+` / `rb+`: read and write an existing file. Reserved; not yet
    /// produced by `FromStr`.
    ReadUpdate(bool),
    /// `w` / `wb`: write-only; truncate the file (creating it if missing) on open.
    Write(bool),
    /// `w+` / `wb+`: read and write; truncate the file (creating it if missing).
    /// Reserved; not yet produced by `FromStr`.
    WriteUpdate(bool),
    /// `a` / `ab`: write-only appending; create the file if missing, preserving content.
    Append(bool),
    /// `a+` / `ab+`: read and append; create the file if missing, preserving content.
    /// Reserved; not yet produced by `FromStr`.
    AppendUpdate(bool),
}

A parsed Python open() mode.

This single enum captures everything that matters about how a file was opened: the access pattern (r/w/a and the + update flag) and whether the file is binary. The variant name encodes the access pattern; the bool payload is true for binary and false for text — i.e. Read(true) is 'rb' and Read(false) is 'r'.

Construct one with the FromStr impl (mode_str.parse::<FileMode>()). The original input string is intentionally not preserved; FileMode::as_str rebuilds the canonical CPython form ('r', 'rb+', 'wb', …), matching how CPython itself normalizes input like 'rt''r' and 'r+b''rb+'.

+ update modes (ReadUpdate/WriteUpdate/AppendUpdate) are reserved in the enum so the mode space is fully represented, but FromStr currently rejects them — properly modelling them needs read-position tracking that the file wrapper does not yet implement. Treat the Update variants as unreachable at runtime; do not pattern-match against them as if they were a valid result of parsing user input.

Carried publicly by MontyObject::FileHandle so a host servicing file operations can inspect the mode without re-parsing the raw string.

as_str

pub fn as_str(&self) -> &'static str

Returns the canonical Python open() mode string for this mode, matching what CPython exposes via file.mode.

The result is always one of the 12 well-formed mode strings (r, rb, r+, rb+, w, wb, w+, wb+, a, ab, a+, ab+). This is the canonical form CPython itself normalizes user input into — e.g. 'rt''r', 'r+b''rb+', 'br''rb'.

is_binary

pub fn is_binary(&self) -> bool

Whether the file is binary ('rb', 'wb', …) rather than text.

readable

pub fn readable(&self) -> bool

Whether read() is allowed by this mode.

writable

pub fn writable(&self) -> bool

Whether write() is allowed by this mode.

is_append

pub fn is_append(&self) -> bool

Whether writes should always append (a/a+).

truncate

pub fn truncate(&self) -> bool

Whether open() must truncate the file to empty immediately (w/w+).

create

pub fn create(&self) -> bool

Whether open() must create the file immediately if missing.

True for the w/w+ and a/a+ families. For append modes this must not disturb existing content.

type_name

pub fn type_name(&self) -> &'static str

Returns the bare Python type name (type(f).__name__) for this mode.

file_type_name

pub fn file_type_name(&self) -> &'static str

Returns the fully-qualified _io wrapper type name a file opened with this mode presents as, matching CPython’s repr(f) (e.g. "_io.TextIOWrapper"). The module-less form is type_name.

Implements: Clone, Copy, Debug, Deserialize<'de>, Eq, FromStr, Hash, PartialEq, Serialize, StructuralPartialEq, ToMontyObject.

FormatFloat

pub struct FormatFloat(pub f64);

A Display adapter that writes a float exactly as CPython’s repr()/str() (identical for floats in Python 3): the shortest decimal string that round-trips, switching to scientific notation when the base-10 exponent is < -4 or >= 16, and always keeping at least one fractional digit (1.0, never 1) — 1e16"1e+16", 1234.5"1234.5", inf/nan lowercased.

This is the default rendering for a bare f"{x}", str(x), repr(x) and floats inside container reprs — not the format mini-language (that’s format_float_g et al, in monty). Rust can’t do this directly: its f64 Display never uses scientific notation (1e16 prints as 10000000000000000) and renders NaN as "NaN".

As a Display adapter it writes straight to the caller’s sink with no heap allocation: it borrows Rust’s shortest-digits guarantee via {:e} into a small stack buffer (an f64 {:e} is ASCII and ≤ 24 bytes) and re-lays-out those digits per CPython’s rules.

Implements: Display.

StringRepr

pub struct StringRepr<'a>(pub &'a str);

Formatter for a Python repr() string.

Implements: Debug, Display.

bytes_repr

pub fn bytes_repr(bytes: &[u8]) -> String;

Returns a CPython-compatible repr string for bytes.

Convenience wrapper around bytes_repr_fmt that returns an owned String.

bytes_repr_fmt

pub fn bytes_repr_fmt(bytes: &[u8], f: &mut impl Write) -> fmt::Result;

Writes a CPython-compatible repr string for bytes to a formatter.

Format: b'...' or b"..." depending on content.

  • Uses single quotes by default
  • Switches to double quotes if bytes contain ' but not "
  • Escapes: \\, \t, \n, \r, \xNN for non-printable bytes

string_repr_fmt

pub fn string_repr_fmt(s: &str, f: &mut impl Write) -> fmt::Result;

Writes a Python repr() string for a given string slice to a formatter.

Quote choice matches CPython: single quotes by default, switching to double quotes only when the string contains a ' but no " (so the quote needn’t be escaped). Backslash, the active quote, and \n/\t/\r use the short escapes; any other non-printable character is escaped numerically (\xNN/\uNNNN/\UNNNNNNNN), e.g. repr('\x00') == "'\\x00'" and repr('\xa0') == "'\\xa0'".

“Non-printable” matches CPython’s str.isprintable (see repr_needs_escape): Unicode categories C* and Z*, except the ASCII space. Category data comes from unicode-general-category, whose Unicode version may differ slightly from CPython’s, affecting only recently (re)assigned code points.

utf8_error_reason

pub fn utf8_error_reason(first_bad_byte: u8, error_len: Option<usize>) -> &'static str;

Classifies an invalid-UTF-8 error into CPython’s reason wording, from the first unexpected byte and Utf8Error::error_len().

error_len == None means the input ended mid-sequence (unexpected end of data); otherwise a byte that is a legal multi-byte lead (0xC2–0xF4) was followed by an invalid continuation, and anything else (stray continuation bytes, the overlong leads 0xC0/0xC1, 0xF5–0xFF) is an invalid start byte. Public (re-exported at the crate root) so monty-fs produces identical wording for text-mode file reads.

DEFAULT_MAX_PRINT_COLLECT_BYTES

pub const DEFAULT_MAX_PRINT_COLLECT_BYTES: usize = 10_485_760usize;

Default cap for PrintWriter::CollectString / PrintWriter::CollectStreams and the matching Python collectors.

Host-side print buffers sit outside ResourceLimits::max_memory; without a cap, a print loop can OOM the host while sandbox limits stay green. Pass max_bytes: None to opt out on trusted hosts.

PrintStream

pub enum PrintStream {
    /// Standard output — the default for every `print()` call today.
    Stdout,
    /// Standard error — reserved for future `print(..., file=sys.stderr)` support.
    Stderr,
}

Identifies the output stream for a single print fragment.

Today the print() builtin only writes to Stdout. The Stderr variant is included for forward compatibility with a future print(..., file=sys.stderr) implementation so the collected-output API shape does not have to change when that lands.

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

PrintWriter

pub enum PrintWriter<'a> {
    /// Silently discard all output.
    Disabled,
    /// Write to standard output.
    Stdout,
    /// Collect all output into a single `String`, in emit order, with no stream labels.
    ///
    /// Second field: max collected bytes (`None` = unlimited). Exceeding raises
    /// `MemoryError` with the same message as `ResourceError::Memory`.
    CollectString(&'a mut String, Option<usize>),
    /// Collect all output as `(stream, text)` tuples.
    ///
    /// The builtin `print()` implementation calls `stdout_write` for each argument
    /// and `stdout_push` for each separator/terminator. To avoid one tuple per
    /// fragment, this variant appends to the trailing tuple when it already matches
    /// the current stream; a new tuple is only pushed when the stream changes.
    /// So long as every write targets the same stream (the status quo today, since
    /// `print()` only writes to stdout), a single `print(a, b)` call produces one
    /// `(Stdout, "a b\n")` entry — and consecutive prints with `end=''` likewise
    /// merge into a single trailing entry.
    ///
    /// Second field: max collected bytes across all tuples (`None` = unlimited).
    CollectStreams(&'a mut Vec<(PrintStream, String)>, Option<usize>),
    /// Delegate to a custom callback.
    Callback(&'a mut dyn PrintWriterCallback),
}

Output handler for the print() builtin function.

Provides common output modes as enum variants to avoid trait object overhead in the typical cases (stdout, disabled, collect). For custom output handling, use the Callback variant with a PrintWriterCallback implementation.

Variants

  • Disabled — silently discards all output (useful for benchmarking or suppressing output).
  • Stdout — writes to standard output (the default behavior).
  • CollectString — accumulates output into a target String for programmatic access. No stream labels are preserved; every fragment is appended in the order it was emitted. The Option<usize> is an optional byte cap (None = unlimited); constructors collect_string / default Python collectors use DEFAULT_MAX_PRINT_COLLECT_BYTES.
  • CollectStreams — accumulates output as (stream, text) pairs, merging consecutive same-stream fragments into one tuple. Each write to the same stream extends the trailing entry rather than producing a new one; a new tuple is only pushed when the stream changes. Same optional byte cap as CollectString.
  • Callback — delegates to a user-provided PrintWriterCallback implementation.

collect_string

pub fn collect_string(buf: &mut String) -> PrintWriter<'_>

Collect into buf with the default DEFAULT_MAX_PRINT_COLLECT_BYTES cap.

collect_streams

pub fn collect_streams(buf: &mut Vec<(PrintStream, String)>) -> PrintWriter<'_>

Collect into buf with the default DEFAULT_MAX_PRINT_COLLECT_BYTES cap.

reborrow

pub fn reborrow(&mut self) -> PrintWriter<'_>

Creates a new PrintWriter that reborrows the same underlying target.

This is useful in iterative execution (start/resume loops) where each step takes PrintWriter by value but you want all steps to write to the same output target. The original writer remains valid after the reborrowed copy is dropped.

stdout_write

pub fn stdout_write(&mut self, output: Cow<'_, str>) -> Result<(), MontyException>

Called once for each formatted argument passed to print().

This method writes only the given argument’s text, without adding separators or a trailing newline. Separators (spaces) and the final terminator (newline) are emitted via stdout_push.

stdout_push

pub fn stdout_push(&mut self, end: char) -> Result<(), MontyException>

Appends a single character to the output.

Generally called to add spaces (separators) and newlines (terminators) within print output.

PrintWriterCallback

pub trait PrintWriterCallback {
    fn stdout_write(&mut self, output: Cow<'_, str>) -> Result<(), MontyException>;
    fn stdout_push(&mut self, end: char) -> Result<(), MontyException>;
}

Trait for custom output handling from the print() builtin function.

Implement this trait and pass it via PrintWriter::Callback to capture or redirect print output from sandboxed Python code.

stdout_write

Called once for each formatted argument passed to print().

This method is responsible for writing only the given argument’s text, and must not add separators or a trailing newline. Separators (such as spaces) and the final terminator (such as a newline) are emitted via stdout_push.

Arguments

  • output - The formatted output string for a single argument (without separators or trailing newline).

stdout_push

Add a single character to stdout.

Generally called to add spaces and newlines within print output.

Arguments

  • end - The character to print after the formatted output.

check_print_collect_limit

pub fn check_print_collect_limit(
    current_len: usize,
    add: usize,
    max_bytes: Option<usize>,
) -> Result<(), MontyException>;

Rejects a collect-buffer growth that would exceed max_bytes.

None means unlimited. On overflow, returns the same MemoryError message as ResourceError::Memory so hosts see one familiar limit string.

ConversionError

pub struct ConversionError {
    /// The type name that was expected (e.g., "int", "str").
    pub expected: &'static str,
    /// The actual type name of the `MontyObject` (e.g., "list", "NoneType",
    /// or a class instance's class name).
    pub actual: String,
}

Error returned when a MontyObject cannot be converted to the requested Rust type.

This error is returned by the TryFrom implementations when attempting to extract a specific type from a MontyObject that holds a different variant.

new

pub fn new(expected: &'static str, actual: impl Into<String>) -> Self

Creates a new ConversionError with the expected and actual type names.

Implements: Debug, Display, Error.

DictPairs

pub struct DictPairs(/* private */);

A collection of key-value pairs representing Python dictionary contents.

Used internally by MontyObject::Dict to store dictionary entries while preserving insertion order. Keys and values are both MontyObject instances.

len

pub fn len(&self) -> usize

Number of (key, value) pairs held by this dict.

is_empty

pub fn is_empty(&self) -> bool

Whether this dict has no pairs.

iter

pub fn iter(&self) -> impl Iterator<Item = &(MontyObject, MontyObject)>

Iterates the (key, value) pairs in insertion order.

Implements: Clone, Debug, Default, Deserialize<'de>, Eq, From<Vec<(MontyObject, MontyObject)>>, FromIterator<(MontyObject, MontyObject)>, IntoIterator, PartialEq, Serialize, StructuralPartialEq.

InvalidInputError

pub enum InvalidInputError {
    /// The input type is not valid for conversion to a runtime Value.
    /// Message explaining why the type is invalid.
    InvalidType(std::borrow::Cow<'static, str>),
    /// A resource limit was exceeded during conversion.
    Resource(ResourceError),
}

Error returned when a MontyObject cannot be used as an input to code execution.

This can occur when:

  • A MontyObject variant (like Repr) is only valid as an output, not an input
  • A resource limit is exceeded during conversion

invalid_type

pub fn invalid_type(msg: impl Into<Cow<'static, str>>) -> Self

Creates a new InvalidInputError for the given type name.

Implements: Clone, Debug, Display, Error, From<ResourceError>.

MAX_TIMEZONE_OFFSET_SECONDS

pub const MAX_TIMEZONE_OFFSET_SECONDS: i32 = 86_399;

Largest UTC offset datetime.timezone accepts, +23:59:59.

See MIN_TIMEZONE_OFFSET_SECONDS.

MIN_TIMEZONE_OFFSET_SECONDS

pub const MIN_TIMEZONE_OFFSET_SECONDS: i32 = -86_399;

Smallest UTC offset datetime.timezone accepts, -23:59:59.

CPython requires an offset strictly inside ±24 hours. Shared with the wire decoder so a forged offset is rejected at the boundary rather than by the sandbox-side constructor, which by then can only report a generic bad value.

MontyClassInstance

pub struct MontyClassInstance {
    /// The instance's class (never a builtin type).
    pub class_type: MontyClassType,
    /// Identity of the instance, generated by whichever side defined it.
    pub instance_id: MontyUuid,
    /// Eagerly-sent attribute name -> value mapping, in order.
    pub attrs: DictPairs,
}

A class instance crossing the sandbox boundary — the payload of MontyObject::ClassInstance.

Host-backed instances carry a host-generated uuid as instance_id, so method calls and lazy attribute lookups on names missing from attrs suspend back to the host, routed by that id (public names only). Sandbox-defined instances carry a worker-generated uuid instead; either way the id never encodes a memory address.

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

MontyClassType

pub struct MontyClassType {
    /// The Python-visible class name (e.g. `"Point"`).
    pub name: String,
    /// Identity of the class, generated by whichever side defined it.
    pub id: MontyUuid,
    /// True for a host-defined class (wire origin `HOST`); false for a
    /// sandbox-defined class (`SANDBOX`). Informational: the sandbox resolves
    /// `id` against its live objects either way, and consults this only to
    /// reject a sandbox id it no longer knows. Builtins never use `MontyClassType`.
    pub host_defined: bool,
    /// Whether `dataclasses.is_dataclass` is true for the class.
    pub is_dataclass: bool,
    /// Class attributes sent eagerly with the type object (class constants,
    /// per the sending wrapper's policy), on every crossing of the class as a
    /// value or as the type branch of an instance. The sandbox keeps one type
    /// object per class id: a non-empty set replaces its attrs, an empty set
    /// (no policy, or a type crossing out) leaves them unchanged.
    pub attrs: DictPairs,
}

A non-builtin class type object crossing the sandbox boundary — the payload of MontyType::Instance and the class half of MontyClassInstance.

id is generated by whichever side defined the class (host uuid4, or a worker uuid for sandbox classes); the sandbox keys its single type object per class on it and routes instantiation and classmethod calls by it. It never encodes an address. PartialEq compares every field, attrs included, not just id.

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

MontyDate

pub struct MontyDate {
    /// Gregorian year in range 1..=9999.
    pub year: i32,
    /// Month component in range 1..=12.
    pub month: u8,
    /// Day component valid for the given month/year.
    pub day: u8,
}

A Python datetime.date value with year, month, and day components.

Implements: Clone, Debug, Deserialize<'de>, Eq, Hash, PartialEq, Serialize, StructuralPartialEq.

MontyDateTime

pub struct MontyDateTime {
    /// Gregorian year in range 1..=9999.
    pub year: i32,
    /// Month component in range 1..=12.
    pub month: u8,
    /// Day component valid for the given month/year.
    pub day: u8,
    /// Hour in range 0..=23.
    pub hour: u8,
    /// Minute in range 0..=59.
    pub minute: u8,
    /// Second in range 0..=59.
    pub second: u8,
    /// Microsecond in range 0..=999_999.
    pub microsecond: u32,
    /// Fixed offset seconds for aware datetimes, or `None` for naive values.
    ///
    /// Within `MIN_TIMEZONE_OFFSET_SECONDS`..=`MAX_TIMEZONE_OFFSET_SECONDS` when set.
    pub offset_seconds: Option<i32>,
    /// Optional explicit timezone name for aware datetimes.
    ///
    /// Must be `None` when `offset_seconds` is `None`.
    pub timezone_name: Option<String>,
}

A Python datetime.datetime value with date, time, and optional timezone components.

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

MontyFileHandle

pub struct MontyFileHandle {
    /// The virtual (sandbox) path of the file. Never a host path.
    pub path: String,
    /// The parsed `open()` mode.
    pub mode: FileMode,
    /// Position for sized/line/seek operations: char index in text mode,
    /// byte index in binary mode. `0` for a freshly opened file.
    pub position: u64,
}

An open file object (the result of open()).

This is the boundary representation of Monty’s heap OpenFile wrapper. It carries everything needed to service a file operation from a host that holds no live OS handle: the virtual path, the mode, and the byte position for seek-aware reads.

The host produces a FileHandle as the result of an OsFunctionCall::Open call; the interpreter then builds its heap file wrapper from it. Conversely, a heap file object passed as an argument to a read/write OS call is converted back to a FileHandle so the host receives this state.

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

MontyObject

pub enum MontyObject {
    /// Python's `Ellipsis` singleton (`...`).
    Ellipsis,
    /// Python's `NotImplemented` singleton.
    NotImplemented,
    /// Python's `None` singleton.
    None,
    /// Python boolean (`True` or `False`).
    Bool(bool),
    /// Python integer (64-bit signed).
    Int(i64),
    /// Python arbitrary-precision integer (larger than i64).
    BigInt(num_bigint::BigInt),
    /// Python float (64-bit IEEE 754).
    Float(f64),
    /// Python string (UTF-8).
    String(String),
    /// Python bytes object.
    Bytes(Vec<u8>),
    /// Python list (mutable sequence).
    List(Vec<Self>),
    /// Python tuple (immutable sequence).
    Tuple(Vec<Self>),
    /// Python named tuple (immutable sequence with named fields).
    ///
    /// Named tuples behave like tuples but also support attribute access by field name.
    /// The type_name is used in repr (e.g., "os.stat_result"), and field_names provides
    /// the attribute names for each position.
    NamedTuple { type_name: String, field_names: Vec<String>, values: Vec<Self> },
    /// Python dictionary (insertion-ordered mapping).
    Dict(DictPairs),
    /// Python set (mutable, unordered collection of unique elements).
    Set(Vec<Self>),
    /// Python frozenset (immutable, unordered collection of unique elements).
    FrozenSet(Vec<Self>),
    /// Python `datetime.date`.
    Date(MontyDate),
    /// Python `datetime.datetime`.
    DateTime(MontyDateTime),
    /// Python `datetime.time`.
    Time(MontyTime),
    /// Python `datetime.timedelta`.
    TimeDelta(MontyTimeDelta),
    /// Python `datetime.timezone` fixed-offset timezone.
    TimeZone(MontyTimeZone),
    /// Python exception with type and optional message argument.
    Exception { exc_type: ExcType, arg: Option<String> },
    /// A Python type object (e.g., `int`, `str`, `list`).
    ///
    /// Returned by the `type()` builtin and can be compared with other types.
    Type(MontyType),
    BuiltinFunction(BuiltinsFunctions),
    /// Python `pathlib.Path` object (or technically a `PurePosixPath`).
    ///
    /// Represents a filesystem path. Can be used both as input (from host) and output.
    Path(String),
    /// An open file object (the result of `open()`).
    FileHandle(MontyFileHandle),
    /// A class instance crossing the sandbox boundary (see `MontyClassInstance`).
    /// Boxed: the payload is larger than every other variant and would grow
    /// `MontyObject` (and so every container element) otherwise.
    ClassInstance(Box<MontyClassInstance>),
    /// An external function provided by the host.
    ///
    /// Returned by the host in response to a `NameLookup` to provide a callable
    /// that the VM can invoke. When called, the VM yields `FunctionCall` to the host.
    Function { name: String, docstring: Option<String> },
    /// Fallback for values that cannot be represented as other variants.
    ///
    /// Contains the `repr()` string of the original value.
    ///
    /// This is output-only and cannot be used as an input to the interpreter.
    Repr(String),
    /// Represents a cycle detected during Value-to-MontyObject conversion.
    ///
    /// When converting cyclic structures (e.g., `a = []; a.append(a)`), this variant
    /// is used to break the infinite recursion. Contains an opaque identity token
    /// (the raw heap index of the object the cycle points back to — meaningful only
    /// for equality, and only within the result that produced it) and the
    /// type-specific placeholder string (e.g., `"[...]"` for lists, `"{...}"` for
    /// dicts). Two `Cycle` values compare equal if they refer to the same object.
    ///
    /// This is output-only and cannot be used as an input to the interpreter.
    Cycle(usize, String),
}

An owned Python value exchanged between Monty and its host.

Construct MontyObject values to provide globals, external-function results, and other inputs to sandboxed code. Execution results and values passed to host callbacks use the same representation.

Most common Python values have a direct variant, including nested collections and datetime values. Repr and Cycle can only appear in output because they cannot be reconstructed as executable Python values. Exception can be used both to raise an exception and to represent one returned by execution.

Collections are owned snapshots: modifying a returned MontyObject does not modify the corresponding value in a running session.

Hashability

Only immutable variants implement Hash, including the datetime family (Date, DateTime, TimeDelta, TimeZone). Attempting to hash mutable variants (List, Dict) will panic.

Serialization

The derived Serialize / Deserialize impls use an externally tagged format ({"Int": 42}, {"String": "hi"}, …). This is what postcard and serde_json::to_string(&obj) produce. It is lossless and designed for snapshots and binary transport, not for human-facing JSON.

dict

pub fn dict(dict: impl Into<DictPairs>) -> Self

Creates a new MontyObject from something that can be converted into a DictPairs.

builtin_function_from_name

pub fn builtin_function_from_name(name: &str) -> Option<Self>

Resolves a builtin function by its Python name (e.g. "len").

The BuiltinsFunctions enum inside MontyObject::BuiltinFunction is crate-private, so boundaries that serialize a builtin function by name (e.g. the subprocess wire protocol) use this to reconstruct the variant. The name matches the variant’s Display output.

host_base_size

pub const fn host_base_size() -> usize

Returns the fixed host footprint charged for each decoded object.

host_metadata_string_size

pub const fn host_metadata_string_size(value: &str) -> usize

Returns the host footprint of one owned string in a metadata vector.

host_size

pub fn host_size(&self) -> usize

Shallow host footprint of a freshly decoded obj: the fixed MontyObject size plus any leaf payload it owns directly (string/bytes/bigint bytes, and the Vec<String> field names of structured values, which aren’t themselves MontyObjects and would otherwise be uncharged). Boxed payloads (ClassInstance, Type(Instance)) charge their heap allocation plus the class name; their eager attrs are charged like container elements. Container elements are excluded — each charges its own size via monty-proto’s decode_field, so a list charges size_of::<MontyObject>() here.

deep_host_size

pub fn deep_host_size(&self) -> usize

Returns the recursively expanded host footprint used by transport budgets.

Unlike Self::host_size, this includes every value stored in a container.

py_repr

pub fn py_repr(&self) -> String

Returns the Python repr() string for this value.

Panics

Could panic if out of memory.

is_truthy

pub fn is_truthy(&self) -> bool

Returns true if this value is “truthy” according to Python’s truth testing rules.

In Python, the following values are considered falsy:

  • None and Ellipsis
  • False
  • Zero numeric values (0, 0.0)
  • Empty sequences and collections ("", b"", [], (), {})

All other values are truthy, including Exception and Repr variants.

type_name

pub fn type_name(&self) -> &str

Returns the Python type name for this value (e.g., "int", "str", "list").

These are the same names returned by Python’s type(x).__name__; a class instance reports its class name ("Point").

Implements: AsRef<MontyObject>, Clone, Debug, Deserialize<'de>, Display, Eq, From<MontyObject>, Hash, PartialEq, Serialize, ToMontyObject, TryFrom<&MontyObject>.

MontyTime

pub struct MontyTime {
    /// Hour in range 0..=23.
    pub hour: u8,
    /// Minute in range 0..=59.
    pub minute: u8,
    /// Second in range 0..=59.
    pub second: u8,
    /// Microsecond in range 0..=999_999.
    pub microsecond: u32,
    /// Fixed offset seconds for aware times, or `None` for naive values.
    ///
    /// Within `MIN_TIMEZONE_OFFSET_SECONDS`..=`MAX_TIMEZONE_OFFSET_SECONDS` when set.
    pub offset_seconds: Option<i32>,
    /// Optional explicit timezone name for aware times.
    ///
    /// Must be `None` when `offset_seconds` is `None`.
    pub timezone_name: Option<String>,
    /// Fold flag, 0 or 1.
    pub fold: u8,
}

A Python datetime.time value: a wall clock with no date attached.

fold is carried so the flag survives the boundary, but neither monty nor this type interprets it — as in CPython it takes no part in equality.

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

MontyTimeDelta

pub struct MontyTimeDelta {
    /// Day component.
    pub days: i32,
    /// Seconds component in normalized range 0..86400.
    pub seconds: i32,
    /// Microseconds component in normalized range 0..1_000_000.
    pub microseconds: i32,
}

A Python datetime.timedelta value representing a duration.

Implements: Clone, Debug, Deserialize<'de>, Eq, Hash, PartialEq, Serialize, StructuralPartialEq.

MontyTimeZone

pub struct MontyTimeZone {
    /// Fixed UTC offset in seconds, within `MIN_TIMEZONE_OFFSET_SECONDS`..=`MAX_TIMEZONE_OFFSET_SECONDS`.
    pub offset_seconds: i32,
    /// Optional display name.
    pub name: Option<String>,
}

A Python datetime.timezone fixed-offset timezone.

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

MontyType

pub enum MontyType {
    Ellipsis,
    Type,
    NoneType,
    Bool,
    Int,
    Float,
    Range,
    Slice,
    /// The four `datetime` classes carry the qualified names the runtime
    /// `Type` uses (`datetime.date`, ...) rather than bare `date`, so a type
    /// object keeps one name either side of the boundary.
    Date,
    DateTime,
    TimeDelta,
    TimeZone,
    Str,
    Bytes,
    List,
    /// `collections.deque`. Qualified like `datetime.datetime` so the
    /// host-boundary name matches the runtime `Type::Deque` (`collections.deque`)
    /// rather than a bare `deque`.
    Deque,
    ListIterator,
    CallableIterator,
    Tuple,
    NamedTuple,
    Dict,
    DictKeys,
    DictItems,
    DictValues,
    Set,
    FrozenSet,
    /// A non-builtin class type object — a sandbox-defined or host-defined
    /// class, carrying the resolved `MontyClassType` (name, uuid, flags).
    /// Sandbox class types are output-only (rejected as inputs); host class
    /// types round-trip.
    ///
    /// `#[strum(disabled)]`: excluded from `EnumIter` (no meaningful default
    /// name; the name round-trip tests iterate the nameable variants only).
    Instance(Box<MontyClassType>),
    /// Exception types render/parse via `ExcType`'s own strum name
    /// (`"ValueError"`, `"json.JSONDecodeError"`, ...), so this variant is
    /// `#[strum(disabled)]`: `name` and
    /// `from_type_name` peel `Exception` off
    /// explicitly.
    Exception(ExcType),
    Function,
    BuiltinFunction,
    Cell,
    Iterator,
    Coroutine,
    Module,
    TextIOWrapper,
    BufferedReader,
    BufferedWriter,
    BufferedRandom,
    SpecialForm,
    Path,
    Property,
    RePattern,
    ReMatch,
    TupleIterator,
    StrAsciiIterator,
    StrIterator,
    BytesIterator,
    RangeIterator,
    DictKeyIterator,
    DictItemIterator,
    DictValueIterator,
    SetIterator,
    ItertoolsCount,
    ItertoolsRepeat,
    /// A `dataclasses.Field` describing one field of a sandbox `@dataclass`,
    /// as found in a class's `__dataclass_fields__`.
    Field,
    ItertoolsPairwise,
    ItertoolsCompress,
    ItertoolsIslice,
    ItertoolsChain,
    ItertoolsCycle,
    NotImplementedType,
    /// The `__dataclass_params__` of a sandbox `@dataclass`: the options it was
    /// decorated with, named as CPython's private class reports itself.
    DataclassParams,
    ItertoolsTakeWhile,
    ItertoolsDropWhile,
    ItertoolsFilterFalse,
    ItertoolsStarMap,
    /// The builtin `object`, which the sandbox exposes as a name only — it is
    /// not a base class and cannot be constructed.
    Object,
    Time,
    /// `functools.partial`, qualified the way CPython's `tp_name` is.
    Partial,
}

The Python type of a value at the host boundary — the public mirror of the internal runtime Type enum.

Where the runtime Type::Instance carries a transient heap id, the public MontyType::Instance carries the resolved MontyClassType (name, uuid, flags), so a MontyType is always self-contained: it can be serialized, sent over the subprocess wire protocol, and displayed without heap access.

A sandbox class type is output-only: its class binding cannot be reconstructed host-side, so passing one back as an input is rejected with an InvalidInputError. Host class types round-trip.

name

pub fn name(&self) -> &str

The Python-visible name of this type ("int", "datetime.datetime", "ValueError", or the class name for Instance).

from_type_name

pub fn from_type_name(name: &str) -> Option<Self>

Parses a name produced by Display/name back to the MontyType — the wire-protocol decode path for builtin type names. Never yields Instance: class names return None (the wire carries instance types in a dedicated field instead), and "object" parses to the builtin Object.

EnumString parses via the same strum serialize attributes that IntoStaticStr renders with, so the two stay in lockstep by construction. Exception types display as their exception name (“ValueError”, “json.JSONDecodeError”, …) — fall back to the ExcType parser.

Implements: Clone, Debug, Deserialize<'de>, Display, Eq, From<&'_derivative_strum MontyType>, From<MontyType>, FromStr, IntoEnumIterator, PartialEq, Serialize, StructuralPartialEq, TryFrom<&str>, VariantNames.

GetenvArgs

pub struct GetenvArgs {
    pub key: String,
    pub default: MontyObject,
}

os.getenv(key, default=None) shape. The host decides whether to substitute default when the variable is unset.

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

MkdirCallArgs

pub struct MkdirCallArgs {
    pub path: MontyPath,
    pub parents: bool,
    pub exist_ok: bool,
}

mkdir(path, parents=False, exist_ok=False) shape. parents/exist_ok are kw-only so ToArgs emits them as kwargs (matching CPython).

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

MontyPath

pub struct MontyPath(/* private */);

Owned virtual (sandbox) path carried by OS-call args.

String newtype: derefs to &str for fs/ routing, and ToMontyObject projects it back to MontyObject::Path at the host boundary. Constructed at the producer site after the source Value has been validated as a path/string — never from raw input.

new

pub fn new(path: String) -> Self

as_str

pub fn as_str(&self) -> &str

into_string

pub fn into_string(self) -> String

Implements: Clone, Debug, Deref, Deserialize<'de>, Eq, From<&str>, From<String>, PartialEq, Serialize, StructuralPartialEq, ToMontyObject.

OpenCallArgs

pub struct OpenCallArgs {
    pub path: MontyPath,
    pub mode: FileMode,
}

open(path, mode) shape. The mode is parsed into FileMode before construction so the fs/ backend doesn’t re-parse; ToArgs re-serialises it back to a MontyObject::String for the host.

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

OsFunctionCall

pub enum OsFunctionCall {
    /// Check if a path exists.
    Exists(MontyPath),
    /// Check if path is a regular file.
    IsFile(MontyPath),
    /// Check if path is a directory.
    IsDir(MontyPath),
    /// Check if path is a symbolic link.
    IsSymlink(MontyPath),
    /// Read file contents as text.
    ReadText(MontyPath),
    /// Read file contents as bytes.
    ReadBytes(MontyPath),
    /// `stat()` — return a stat result tuple.
    Stat(MontyPath),
    /// List directory contents.
    Iterdir(MontyPath),
    /// Resolve symlinks and return absolute path.
    Resolve(MontyPath),
    /// Absolute path without symlink resolution.
    Absolute(MontyPath),
    /// Write text to file (truncating).
    WriteText(PathStringDataArgs),
    /// Append text to file.
    AppendText(PathStringDataArgs),
    /// Write bytes to file (truncating).
    WriteBytes(PathBytesDataArgs),
    /// Append bytes to file.
    AppendBytes(PathBytesDataArgs),
    /// Open a file. The host performs the open-time effect (truncate for
    /// `w`/`w+`, create-if-missing for `a`/`a+`, existence check for `r`/`r+`)
    /// and returns a `MontyObject::FileHandle` — it never holds a live OS
    /// handle across calls.
    Open(OpenCallArgs),
    /// Create directory (`parents`/`exist_ok` kwargs).
    Mkdir(MkdirCallArgs),
    /// Remove file.
    Unlink(MontyPath),
    /// Remove directory.
    Rmdir(MontyPath),
    /// Rename / move (src → dst).
    Rename(RenameCallArgs),
    /// Get an environment variable value.
    Getenv(GetenvArgs),
    /// Get the entire environment as a dictionary.
    GetEnviron,
    /// Get today's date from the host system (for `date.today()`).
    DateToday,
    /// Get the current date/time from the host system (for `datetime.now(tz=...)`).
    /// Carries the timezone argument, `None` for a naive result.
    DateTimeNow(Option<MontyTimeZone>),
}

Tagged dispatch value for OS-level operations.

Each variant carries the strongly-typed args/kwargs the corresponding OS call needs. The fs/ layer matches on this enum directly (no MontyObject introspection); host bindings get a generic (positional, keyword) view via OsFunctionCall::to_args.

See the module docs for how to add a new variant.

name

pub fn name(&self) -> &'static str

Stable string name for this OS function — surfaces in Self::on_no_handler errors, host os callbacks, and serialised snapshots. The strum serialize string on each variant.

to_args

pub fn to_args(self) -> (Vec<MontyObject>, Vec<(MontyObject, MontyObject)>)

Projects this call’s args into (positional, keyword) MontyObject vectors for delivery to a host callback.

is_write

pub fn is_write(&self) -> bool

Whether this call mutates filesystem state — the read-only-mount gate. Open’s write-ness is mode-dependent (w/w+/a/a+ write; r/r+ don’t).

is_existence_check

pub fn is_existence_check(&self) -> bool

Whether this operation checks existence without reading content. Existence checks return false for nonexistent paths rather than raising FileNotFoundError, matching CPython’s pathlib.Path.

embedded_null_message

pub fn embedded_null_message(&self, for_destination: bool) -> &'static str

CPython’s ValueError message for a path containing a null byte.

The wording is not uniform in CPython: it comes from whichever layer first inspects the path, so the content operations go through open() and say embedded null byte, while the metadata ones are named by the syscall their os wrapper was about to make. for_destination picks the rename argument that carried the byte.

fs_primary_path

pub fn fs_primary_path(&self) -> Option<&str>

The call’s primary path if it’s a FS operation, None otherwise.

Used for routing and error reporting.

rename_destination

pub fn rename_destination(&self) -> Option<&str>

The rename destination path, or None for every other variant — the second routing key a mount table needs (both rename endpoints must resolve to the same mount).

on_no_handler

pub fn on_no_handler(&self) -> MontyException

Exception to raise when no handler accepted this call: PermissionError for FS ops (with the path), RuntimeError for non-FS ops.

Implements: Clone, Debug, Deserialize<'de>, Display, From<&'_derivative_strum OsFunctionCall>, From<OsFunctionCall>, Serialize.

PathBytesDataArgs

pub struct PathBytesDataArgs {
    pub path: MontyPath,
    pub data: Vec<u8>,
}

path + bytes data shape used by WriteBytes and AppendBytes.

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

PathStringDataArgs

pub struct PathStringDataArgs {
    pub path: MontyPath,
    pub data: String,
}

path + str data shape used by WriteText and AppendText.

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

RenameCallArgs

pub struct RenameCallArgs {
    pub src: MontyPath,
    pub dst: MontyPath,
}

rename(src, dst) shape.

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

dir_stat

pub fn dir_stat(mode: i64, mtime: f64) -> MontyObject;

Creates a stat_result for a directory.

The directory type bits (0o040_000) are automatically added if not present.

Arguments

  • mode - Directory permissions as octal. Common values:
    • 0o755 - rwxr-xr-x (owner full, others read/execute)
    • 0o700 - rwx------ (owner only)
    • 0o040755 - same as 0o755 with explicit directory type bits
  • mtime - Modification time as Unix timestamp

file_stat

pub fn file_stat(mode: i64, size: i64, mtime: f64) -> MontyObject;

Creates a stat_result for a regular file.

The file type bits (0o100_000) are automatically added if not present.

Arguments

  • mode - File permissions as octal. Common values:
    • 0o644 - rw-r—r— (owner read/write, others read)
    • 0o600 - rw------- (owner read/write only)
    • 0o755 - rwxr-xr-x (executable, owner full, others read/execute)
    • 0o100644 - same as 0o644 with explicit file type bits
  • size - File size in bytes
  • mtime - Modification time as Unix timestamp

stat_result

pub fn stat_result(
    st_mode: i64,
    st_ino: i64,
    st_dev: i64,
    st_nlink: i64,
    st_uid: i64,
    st_gid: i64,
    st_size: i64,
    st_atime: f64,
    st_mtime: f64,
    st_ctime: f64,
) -> MontyObject;

Creates a full stat_result with all 10 fields specified.

This is the low-level builder; prefer file_stat(), dir_stat(), or symlink_stat() for common cases.

pub fn symlink_stat(mode: i64, mtime: f64) -> MontyObject;

Creates a stat_result for a symbolic link.

The symlink type bits (0o120_000) are automatically added if not present.

Arguments

  • mode - Symlink permissions as octal. Common values:
    • 0o777 - rwxrwxrwx (symlinks typically have full permissions)
    • 0o120777 - same as 0o777 with explicit symlink type bits
  • mtime - Modification time as Unix timestamp

BASELINE_MEMORY

pub static BASELINE_MEMORY: std::sync::atomic::AtomicUsize;

The leanest the process has ever been at an arming point: what the worker costs to exist, before any session ran.

DEFAULT_MAX_RECURSION_DEPTH

pub const DEFAULT_MAX_RECURSION_DEPTH: usize = 1000;

Recommended maximum recursion depth if not otherwise specified.

DEFAULT_MAX_SUSPENSIONS

pub const DEFAULT_MAX_SUSPENSIONS: usize = 1000;

Maximum suspensions a host services per session if not otherwise specified: a backstop against a sandbox looping on host calls while max_duration is paused.

LARGE_RESULT_THRESHOLD

pub const LARGE_RESULT_THRESHOLD: usize = 100_000;

Threshold in bytes above which check_large_result is called.

Operations that may produce results larger than this threshold (100KB) should call check_large_result before performing the operation. This prevents DoS attacks where operations like 2 ** 10_000_000 allocate huge amounts of memory before the memory check can catch them.

LIVE_MEMORY

pub static LIVE_MEMORY: std::sync::atomic::AtomicUsize;

Allocator-backed live bytes requested through the global allocator

OOM_EXIT_CODE

pub const OOM_EXIT_CODE: i32 = 65;

Exit code a worker uses when it exceeded its memory limit or the allocator refused an allocation, so the parent can report MemoryError instead of an unclassifiable SIGABRT.

EX_DATAERR from BSD sysexits.h. See https://man.freebsd.org/cgi/man.cgi?query=sysexits.

ResourceError

pub enum ResourceError {
    /// Maximum execution time exceeded.
    Time { limit: std::time::Duration, elapsed: std::time::Duration },
    /// Maximum memory usage exceeded.
    Memory { limit: usize, used: usize },
    /// Maximum recursion depth exceeded.
    Recursion { limit: usize, depth: usize },
}

Error returned when a resource limit is exceeded during execution.

This allows the sandbox to enforce strict limits on execution time and memory usage.

All variants except Recursion are uncatchable inside the sandbox: untrusted code must never intercept resource enforcement. Recursion surfaces as a catchable RecursionError, matching CPython.

Implements: Clone, Debug, Display, Error, From<ResourceError>.

ResourceLimits

pub struct ResourceLimits {
    /// Maximum execution time.
    pub max_duration: Option<std::time::Duration>,
    /// Maximum allocator-backed memory in bytes.
    ///
    /// Requires the executable to install and arm `monty-alloc`.
    pub max_memory: Option<usize>,
    /// Run garbage collection every N GC-tracked allocations.
    pub gc_interval: Option<usize>,
    /// Maximum recursion depth (function call stack depth).
    pub max_recursion_depth: usize,
    /// Maximum suspensions the host may service (default
    /// `DEFAULT_MAX_SUSPENSIONS`; always bounded, like recursion depth).
    /// The interpreter only stores this limit; hosts must enforce it.
    pub max_suspensions: usize,
}

Configuration for resource limits.

The time/memory/GC limits are optional — set to None to disable — but recursion depth and the suspension budget are always bounded (defaults DEFAULT_MAX_RECURSION_DEPTH and DEFAULT_MAX_SUSPENSIONS): unbounded recursion would let sandboxed code overflow the native stack and abort the process, and unbounded suspensions would let it loop on host calls. Use ResourceLimits::default() for the recursion-only defaults, or build custom limits with the builder pattern.

max_duration

pub fn max_duration(self, limit: Duration) -> Self

Sets the maximum execution duration.

max_memory

pub fn max_memory(self, limit: usize) -> Self

Sets allocator-backed maximum memory usage in bytes.

Requires the executable to install and arm monty-alloc; otherwise the limit is silently not enforced.

gc_interval

pub fn gc_interval(self, interval: usize) -> Self

Sets the garbage collection interval (run GC every N GC-tracked allocations).

max_recursion_depth

pub fn max_recursion_depth(self, limit: usize) -> Self

Sets the maximum recursion depth (function call stack depth).

max_suspensions

pub fn max_suspensions(self, limit: usize) -> Self

Sets the host-enforced maximum number of suspensions.

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

ResourceTracker

pub struct ResourceTracker { /* private fields */ }

A resource tracker that enforces configurable limits.

Checks allocator-backed memory usage and tracks execution time, returning errors when limits are exceeded. It also schedules garbage collection.

Uses Cell for mutable timing and recursion state behind shared references.

max_duration limits cumulative execution time: the clock runs only while the VM is executing bytecode (between the outermost on_execution_start/on_execution_stop pair) and is paused while execution is suspended waiting on the host — external function calls, OS callbacks — and between REPL feeds. The accumulated time is serialized, so a deserialized session resumes its budget where it left off rather than restarting from zero.

new

pub fn new(limits: ResourceLimits) -> Self

Creates a new ResourceTracker with the given limits.

The execution-time clock starts at zero and only runs while the VM executes, so the tracker can be created any amount of time before the first run without consuming the duration budget. A configured max_memory requires monty-alloc installed as the global allocator and armed via its set_limit; otherwise it is silently not enforced.

elapsed

pub fn elapsed(&self) -> Duration

Returns the cumulative execution time: bytecode-execution wall time accumulated across runs/feeds, excluding time suspended on the host or idle between feeds. Includes the in-progress window if the VM is currently executing.

max_duration

pub fn max_duration(&self) -> Option<Duration>

Returns the configured maximum cumulative execution time, if any.

max_memory

pub fn max_memory(&self) -> Option<usize>

Returns the configured memory budget, if any. Hosts that bound a worker process from outside the interpreter size that bound from this.

max_suspensions

pub fn max_suspensions(&self) -> usize

Returns the host-enforced suspension budget (default DEFAULT_MAX_SUSPENSIONS; never unlimited).

has_memory_time_limit

pub fn has_memory_time_limit(&self) -> bool

Returns whether the VM has a memory or time limit configured.

set_max_duration

pub fn set_max_duration(&mut self, duration: Duration)

Sets the maximum execution duration as a fresh budget from now, resetting the accumulated execution time to zero.

This lets a host enforce a different (typically shorter) time limit for a resumed phase — e.g. allowing a long build phase, then giving repr() of the result only a few milliseconds. Time spent suspended in the host never counts toward the budget either way.

check_allocation

pub fn check_allocation(&self, additional: usize) -> Result<(), ResourceError>

Checks whether one up-front allocation fits the memory budget.

Use this before reserving a buffer that could cross both the soft and hard allocator limits before execution reaches another checkpoint.

check_memory_time

pub fn check_memory_time(&self) -> Result<(), ResourceError>

Called periodically to check allocator-backed memory and time limits.

Returns Ok(()) while configured limits are respected, or the relevant resource error once either limit is exceeded.

Takes &self rather than &mut self because checking elapsed time is a read-only operation. This allows time checks in contexts that only have an immutable heap reference, such as py_repr_fmt.

check_time

pub fn check_time(&self) -> Result<(), ResourceError>

Called periodically to check the time limit. Elapsed execution time is monotonic, so once the budget is exceeded every later call fails too.

LOOP_CHECK_INTERVAL

pub const LOOP_CHECK_INTERVAL: usize = 64;

Items processed between full checks in amortized per-item Rust loops (see check_time_every). A limit can be overshot by up to this many items’ work before the next check — an accepted trade for cheap loops; the process-level hard limits backstop pathological cases.

check_time_every

pub fn check_time_every(&self, i: usize) -> Result<(), ResourceError>

Amortized per-item time check for Rust-side loops: a full clock read once per LOOP_CHECK_INTERVAL calls, free otherwise. Key i on the loop’s index or a monotonically increasing counter. Fires at the end of each block (i % N == N-1) so loops shorter than the interval pay no clock read at all — the VM dispatch checkpoint covers cadence between short calls.

check_memory_time_every

pub fn check_memory_time_every(&self, i: usize) -> Result<(), ResourceError>

Amortized per-item memory + time check; the memory-probing sibling of check_time_every, for loops that allocate per item. Between full checks the allocator’s hard limit still bounds runaway growth.

check_recursion_depth

pub fn check_recursion_depth(&self, current_depth: usize) -> Result<(), ResourceError>

Called before pushing a new call frame to check recursion depth.

Returns Ok(()) if within recursion limit, or Err(ResourceError::Recursion) if the limit would be exceeded. current_depth is the call stack depth before the new frame is pushed.

check_large_result

pub fn check_large_result(&self, estimated_bytes: usize) -> Result<(), ResourceError>

Called before operations that may produce large results (>100KB).

This allows pre-emptive rejection of operations like 2 ** 10_000_000 before the memory is actually allocated. The check only happens for estimated result sizes above LARGE_RESULT_THRESHOLD to avoid overhead on small operations.

gc_interval

pub fn gc_interval(&self) -> Option<usize>

Returns the configured garbage collection interval, in GC-tracked allocations.

The cycle collector runs at most once per gc_interval GC-tracked allocations, and additionally short-circuits when no cycle candidates are pending — so programs that never form cycles pay no collector cost regardless of their allocation rate. None tells the heap to use its built-in default scheduling threshold.

on_execution_start

pub fn on_execution_start(&self)

Called when the VM enters its execution loop from a host boundary (VM::run_external), starting one execution window.

Paired with on_execution_stop and never nested — VM-internal re-entry (task switches, host-initiated function evaluation) uses the raw run loop, so its time falls inside the enclosing window. The execution-time clock runs between the pair; it is not running while execution is suspended waiting on the host (external function calls) or between feeds.

on_execution_stop

pub fn on_execution_stop(&self)

Called when the VM leaves its execution loop — on completion, error, or suspension at an external call. See on_execution_start.

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

ExtFunctionResult

pub enum ExtFunctionResult {
    /// Continues execution with the return value from the external function.
    Return(MontyObject),
    /// Continues execution with the exception raised by the external function.
    Error(MontyException),
    /// Pending future — the external function is a coroutine.
    ///
    /// The `u32` is the `call_id` from the `FunctionCall` that created this
    /// snapshot. It is used to track the pending future so it can be resolved
    /// later via `ResolveFutures::resume()`.
    Future(u32),
    /// The function was not found, should result in a `NameError` exception.
    NotFound(String),
}

Return value or exception from an external function.

Implements: Debug, From<MontyException>, From<MontyObject>.

NameLookupResult

pub enum NameLookupResult {
    /// The name resolves to this value.
    Value(MontyObject),
    /// The name is undefined — the VM raises `NameError` / `AttributeError`.
    Undefined,
    /// Resolving the name raised this exception on the host; the VM raises
    /// it where the lookup suspended, like a failed external call.
    Error(MontyException),
}

Result of a name lookup from the host.

When the VM encounters an unresolved name (or a lazy attribute on a host-backed object), the host provides one of these:

  • Value(obj): The name resolves to this value (a plain name is cached in its namespace slot; an attribute is re-consulted on every access).
  • Undefined: The name does not exist — NameError for a plain name, AttributeError for an attribute.
  • Error(exc): Resolving it raised on the host — the exception is raised inside the sandbox, so hasattr() / getattr() defaults do not apply.

Implements: Debug, From<MontyException>, From<MontyObject>, From<Option<MontyObject>>.

AssertMessageAnnotations

pub enum AssertMessageAnnotations {
    /// Disable introspection; bare asserts use CPython's empty message.
    Off,
    /// Retain at most this many UTF-8 bytes per operand before any `…` suffix.
    /// Non-zero because `0` encodes `Off` on the wire.
    MaxBytes(std::num::NonZeroU32),
}

Controls the pytest-style introspected assert failure messages of CompileOptions::assert_message_annotations.

The choice is baked in at compile time (whether the introspecting opcodes are emitted) but the truncation limit is applied at runtime, so it also travels with serialized sessions.

DEFAULT_MAX_BYTES

pub const DEFAULT_MAX_BYTES: NonZeroU32 = _;

Operand-repr truncation used by Default and From<bool>.

enabled

pub fn enabled(self) -> bool

Whether the compiler should emit introspecting assert opcodes.

max_bytes

pub fn max_bytes(self) -> u32

Returns the wire value: 0 when disabled, otherwise the UTF-8 byte cap.

from_max_bytes

pub fn from_max_bytes(value: u32) -> Self

Decodes the wire value: 0 is off and any other value is the byte cap.

Implements: Clone, Copy, Debug, Default, Deserialize<'de>, Eq, From<bool>, PartialEq, Serialize, StructuralPartialEq.

CompileOptions

pub struct CompileOptions {
    /// Give failed `assert` statements pytest-style introspected messages,
    /// deliberately diverging from CPython; see `limitations/assert.md`.
    /// On by default with a 120-byte operand-repr truncation.
    pub assert_message_annotations: AssertMessageAnnotations,
}

Options controlling how Monty behavior diverges from plain CPython.

Consumed when code is compiled: a MontyRun bakes the choices into the program at construction, while a MontyRepl stores them so every snippet fed to the session compiles the same way.

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

TypeCheckState

pub struct TypeCheckState {
    /// User-provided stubs plus every snippet that has completed successfully.
    pub committed_stubs: String,
    /// The in-flight snippet; committed on success, discarded on error.
    pub pending_snippet: Option<String>,
    /// How diagnostics are rendered by whoever runs the type checker.
    pub config: TypeCheckingConfig,
}

Per-session type-check state: successfully committed snippets accumulate as stubs so later snippets can reference names defined by earlier ones.

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

TypeCheckingConfig

pub struct TypeCheckingConfig {
    /// Output format.
    pub format: TypeCheckingFormat,
    /// Whether to include ANSI colour escapes. Only `Full` and `Concise`
    /// render any colour; the machine-readable formats ignore it.
    pub color: bool,
}

How a type check renders whatever diagnostics it finds.

Implements: Clone, Copy, Debug, Default, Deserialize<'de>, Eq, PartialEq, Serialize, StructuralPartialEq.

TypeCheckingFormat

pub enum TypeCheckingFormat {
    /// Human-readable diagnostics with a source snippet and carets.
    Full,
    /// One `path:line:col: severity[rule] message` line per diagnostic.
    Concise,
    /// Azure Pipelines logging commands.
    Azure,
    /// A JSON array of diagnostic objects.
    Json,
    /// One JSON diagnostic object per line.
    JsonLines,
    /// Reviewdog diagnostic JSON.
    Rdjson,
    /// Pylint-compatible output.
    Pylint,
    /// GitLab Code Quality report JSON.
    Gitlab,
    /// GitHub Actions workflow commands.
    Github,
}

How type-check diagnostics are rendered into text.

Mirrors ty’s DiagnosticFormat. Rendering happens wherever the type checker runs (inside the worker for pool sessions), because ty’s structured diagnostics borrow the salsa database and cannot cross a process boundary — so the format has to be chosen before the check, not after it.

Serialized into session dumps by discriminant, so new variants must be appended — inserting one shifts every later variant and silently rewrites older dumps’ format (see DUMP_VERSION in monty).

from_name

pub fn from_name(name: &str) -> Result<Self, String>

Parses a format name, reporting the valid names on failure.

Bindings take the format as a string, so the error has to be good enough to show a user who guessed wrong.

names

pub fn names() -> String

Comma-separated list of the accepted format names.

Implements: Clone, Copy, Debug, Default, Deserialize<'de>, Display, Eq, FromStr, PartialEq, Serialize, StructuralPartialEq, TryFrom<&str>, VariantNames.

MontyUuid

pub struct MontyUuid(/* private */);

A 16-byte UUID identifying a host or sandbox class/instance across the sandbox boundary.

from_bytes

pub const fn from_bytes(bytes: [u8; 16]) -> Self

Wraps raw bytes as-is; used when the bytes are already a valid uuid.

from_u128

pub const fn from_u128(v: u128) -> Self

Builds a deterministic id from an integer — for tests and fixtures that must be reproducible; never use for real identity.

from_random_bytes

pub const fn from_random_bytes(bytes: [u8; 16]) -> Self

Stamps the uuid4 version and RFC 4122 variant bits over caller-supplied random bytes, so any entropy source yields a well-formed uuid4.

as_bytes

pub const fn as_bytes(&self) -> &[u8; 16]

Returns the raw bytes (big-endian field order, per RFC 4122).

try_from_slice

pub fn try_from_slice(bytes: &[u8]) -> Option<Self>

Parses exactly 16 bytes; None for any other length. This is the wire-decode entry point, so it must never panic.

parse

pub fn parse(s: &str) -> Option<Self>

Parses the canonical hyphenated form (8-4-4-4-12 hex digits), any case; None on any deviation. Used by the JS surfaces, which carry uuids as strings.

Implements: Clone, Copy, Debug, Deserialize<'de>, Display, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, StructuralPartialEq.