The language subset

This is Monty's subset, ported as-is. It is deliberately smaller than CPython, and the constructs it does not support are rejected at parse time rather than failing halfway through a run, which is the behaviour you want when a model wrote the code.

Supported

Values int, float, bool, str, bytes, list, tuple, dict, set, frozenset, None, slices, ranges
Statements assignment (including augmented, chained and starred unpacking), if / elif / else, for, while, break, continue, try / except / else / finally, raise, with, assert, import, global, nonlocal, pass
Functions positional, keyword, default, *args, **kwargs, keyword-only, nested functions, closures, lambda, recursion to the depth limit
Classes simple classes: instance methods, __init__, __repr__, __str__, class variables
Decorators on classes and on top-level or nested def / async def
Comprehensions list, set, dict, and generator expressions
Strings f-strings including nesting, format specs and =, plus str.format and %
Exceptions the standard hierarchy, custom exception classes, raise … from …

Not supported

Each of these raises at compile time, before any code runs:

  • Class inheritance and metaclasses. class Foo(Bar): is rejected; classes are simple.
  • Decorators on methods, which also rules out @classmethod, @staticmethod and @property.
  • yield and yield from. There are no generator functions. Generator expressions parse, and currently materialise to a list rather than staying lazy.
  • match statements.
  • del statements, in either form.
  • async with, async for and async comprehensions.
  • Exception groups (try* / except*).
  • PEP 695 type aliases.
  • Wildcard imports (from m import *).
  • Complex numbers and t-strings.
Reject early, and say why

An unsupported construct raises a NotImplementedError naming what it was, at compile time. A model handed that message can fix its own code; one that hit the same limitation halfway through a run would have already left side effects behind.

One deliberate divergence

Upstream Monty recycles object slots on its own heap, so a temporary's id() can be handed to the next object of the same shape. Object identity here is the host runtime's and an id is never recycled. It is the single conformance fixture this port does not pass, and it is a difference you would only notice by asserting on id().

© 2026 Curiosity. All rights reserved.