Python Types Supported in Monty: A Complete Guide to the Pydantic Sandbox Runtime

Monty supports a comprehensive subset of Python's data model including primitives (None, bool, int, float), containers (list, dict, set, tuple), callables (functions, builtins), and standard library types like pathlib.Path, all implemented through the Type and Value enums in the Rust source.

Monty is a secure Python interpreter from Pydantic that executes untrusted code in a sandboxed environment. Understanding which Python types are supported in Monty is essential for developers building safe code execution pipelines. This guide examines the complete type system implemented in the Monty Rust codebase, mapping every supported Python type to its underlying enum variants and source file locations.

Primitive and Singleton Types in Monty

Monty implements Python's fundamental singletons and numeric types through dedicated enum variants in crates/monty/src/types/type.rs and crates/monty/src/value.rs.

None, bool, and Numeric Types

The basic building blocks include:

  • None – Represented as Type::NoneType and Value::None (type.rs L32-L33)
  • bool – Type::Bool with Value::Bool(bool) storage (type.rs L33-L34)
  • int – Supports both small integers via Value::Int(i64) and arbitrarily large integers through Value::InternLongInt(LongIntId) (type.rs L34-L35)
  • float – Type::Float with Value::Float(f64) for IEEE 754 double-precision storage (type.rs L35-L36)

Ellipsis and Type Objects

Monty also supports Python's special singletons:

  • Ellipsis (...) – Type::Ellipsis and Value::Ellipsis (type.rs L30-L31)
  • type objects – Type::Type for metaclass operations and isinstance checks (type.rs L31-L32)

Container and Collection Types Supported by Monty

Monty implements Python's core data structures with full mutability semantics and iterator support. Each container type resides in its own module (e.g., list.rs, dict.rs) and stores data on the heap via Value::Ref to keep the enum size small.

Mutable Sequences and Mappings

  • list – Type::List with heap-allocated HeapData::List storage (type.rs L39-L41)
  • dict – Type::Dict with HeapData::Dict for key-value mappings (type.rs L43-L44)
  • set – Type::Set with HeapData::Set for unordered unique collections (type.rs L44-L45)

Immutable Collections and Sequences

  • tuple – Type::Tuple with HeapData::Tuple for immutable sequences (type.rs L40-L42)
  • frozenset – Type::FrozenSet with HeapData::FrozenSet for immutable sets (type.rs L45-L46)
  • range – Type::Range with HeapData::Range for lazy integer sequences (type.rs L36-L37)
  • slice – Type::Slice with HeapData::Slice for indexing operations (type.rs L38-L39)

Text and Binary Data

  • str – Type::Str with HeapData::Str for Unicode strings (type.rs L38-L39)
  • bytes – Type::Bytes with HeapData::Bytes for immutable byte sequences (type.rs L39-L40)

Callable and Iterator Types in Monty

Monty supports Python's callable objects and asynchronous primitives, enabling function definitions, built-in method calls, and async/await patterns.

User-Defined Functions and Builtins

  • function (user-defined) – Type::Function with Value::DefFunction(FunctionId) referencing the function definition table (type.rs L48-L50)
  • builtin_function_or_method – Type::BuiltinFunction with Value::Builtin(Builtins) for sandbox-safe built-in operations like len() or print() (type.rs L49-L51)

Iterators, Coroutines, and Modules

  • iterator – Type::Iterator with HeapData::Iter for objects returned by iter() (type.rs L52-L53)
  • coroutine / external_future – Type::Coroutine with Value::ExternalFuture(CallId) for async/await patterns and external async calls (type.rs L54-L56)
  • module – Type::Module with Value::Ref → HeapData::Module for imported namespace objects (type.rs L55-L56)

Standard Library Types in the Monty Sandbox

Beyond core built-ins, Monty includes carefully selected standard library types that maintain sandbox safety while providing essential functionality.

pathlib.Path and Dataclasses

  • pathlib.Path (POSIX) – Type::Path with HeapData::Path for filesystem path manipulation without unsafe operations (type.rs L63-L65)
  • dataclass – Type::Dataclass with HeapData::Dataclass for the runtime representation of data classes (type.rs L46-L47)

Descriptors, Typing, and I/O

  • property descriptor – Type::Property with Value::Property(Property) for managed attributes (type.rs L66-L68)
  • typing._SpecialForm (e.g., Any, Union) – Type::SpecialForm for generic type hints (type.rs L60-L62)
  • io.TextIOWrapper (stdout/stderr) – Type::TextIOWrapper with Value::Marker(Marker) for stream handling (type.rs L57-L59)

Exception Types

  • Exception classes (e.g., ValueError) – Type::Exception(ExcType) for the exception hierarchy, though notably disabled from EnumString parsing since exceptions cannot be constructed from plain string tokens (type.rs L47-L48)

How Monty Implements Python Type Checking

Monty's type system relies on a dual-enum architecture that separates static type metadata from runtime object representation.

The Type Enum vs Value Enum Architecture

The Type enum in crates/monty/src/types/type.rs defines the static type system used for isinstance checks, type() calls, and constructor dispatch. The Value enum in crates/monty/src/value.rs represents every Python object that can exist at runtime, with small values stored inline and larger containers allocated on the heap via Value::Ref.

This separation allows Monty to perform fast type comparisons using the Type enum while maintaining rich runtime semantics through Value variants like Value::Int(i64) for small integers or Value::Ref pointing to HeapData::List for mutable sequences.

Constructor Dispatch and isinstance Checks

When the parser encounters a literal such as 'hello', 123, or [1, 2], it creates the appropriate Value variant directly. For constructor calls like list(x), int('42'), or str(b), the Type::call method (defined in type.rs lines 72-78) dispatches to the concrete type's init implementation—such as List::init, Str::init, or the Int conversion logic.

The isinstance builtin compares the object's runtime type tag against the Type enum variant, ensuring CPython-compatible behavior while maintaining sandbox safety.

Working with Monty Types: Code Examples

The following examples demonstrate that Monty's supported types behave identically to CPython, whether accessed through the Python bindings (pydantic_monty) or the Rust API.

Using Monty from Python

from pydantic_monty import Monty

code = """

# primitives

a = None
b = True
c = 42
d = 3.14
e = ...

# containers

lst = [1, 2, 3]
tpl = (4, 5)
dct = {'x': 1, 'y': 2}
st = {1, 2, 3}
frz = frozenset([4, 5])
rng = range(0, 10, 2)
slc = slice(1, 5, 2)
bts = b'abc'
txt = "hello"

# callable / iterator

it = iter(lst)
next(it)

# special objects

import pathlib
p = pathlib.Path('.')
p / 'subdir' / 'file.txt'

# show types

print(type(a), type(b), type(c), type(d), type(e))
print(type(lst), type(tpl), type(dct), type(st), type(frz), type(rng), type(slc))
print(type(bts), type(txt))
print(type(it), type(p))
"""

m = Monty(code)
result = m.run()
print(result)

Expected output (matching CPython):

<class 'NoneType'> <class 'bool'> <class 'int'> <class 'float'> <class 'ellipsis'>
<class 'list'> <class 'tuple'> <class 'dict'> <class 'set'> <class 'frozenset'> <class 'range'> <class 'slice'>
<class 'bytes'> <class 'str'>
<class 'range_iterator'> <class 'PosixPath'>

Creating a Monty Sandbox from Rust

For Rust developers embedding the interpreter directly:

use pydantic_monty::Monty;

let src = r#"
x = [1, 2, 3]
print(type(x))   # -> <class 'list'>

"#;

let mut interpreter = Monty::new(src);
interpreter.run().unwrap();

The output matches CPython because the Type enum implements fmt::Display to return the exact Python type names (see impl fmt::Display for Type in type.rs).

Summary

  • Monty supports primitive types including None, bool, int, float, and Ellipsis, with int handling both small values and arbitrarily large integers through separate variants.
  • Container types cover the full Python collection hierarchy: mutable (list, dict, set), immutable (tuple, frozenset, range), and sequence helpers (slice, str, bytes), all heap-allocated via Value::Ref.
  • Callable types include user-defined functions (Type::Function), built-in methods (Type::BuiltinFunction), iterators, coroutines, and modules, enabling complete Python program execution.
  • Standard library extensions such as pathlib.Path, dataclass, property descriptors, and exception types extend the sandbox with safe I/O and data structure capabilities.
  • The dual-enum architecture separates static type checking (Type in type.rs) from runtime object representation (Value in value.rs), with constructor dispatch handled by Type::call methods.

Frequently Asked Questions

Does Monty support Python's arbitrary-precision integers?

Yes. Monty handles arbitrarily large integers through the Value::InternLongInt(LongIntId) variant for values exceeding i64 range, while small integers use the inline Value::Int(i64) representation. This matches CPython's transparent int/long unification.

How does Monty handle mutable versus immutable container types?

Mutable containers like list, dict, and set are stored on the heap via Value::Ref pointing to HeapData variants (e.g., HeapData::List), allowing reference-counted sharing and in-place modification. Immutable types like tuple and frozenset use the same heap storage but enforce immutability through their method implementations in tuple.rs and set.rs.

Can Monty execute Python code that uses type hints from the typing module?

Yes. Monty supports typing._SpecialForm objects including Any, Union, and other generic constructs through the Type::SpecialForm variant. However, these are currently treated as runtime objects rather than participating in static type checking within the sandbox.

What file paths contain the core type definitions in Monty?

The master type system lives in crates/monty/src/types/type.rs (the Type enum for isinstance and constructors) and crates/monty/src/value.rs (the Value enum for runtime object representation). Container implementations are modularized in crates/monty/src/types/list.rs, dict.rs, set.rs, and str.rs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →