docs/limitations/index.md
Monty is not a Python implementation aiming for completeness. It implements enough Python for a model to express what it wants to do, and deliberately stops there. Everything it does implement is meant to behave exactly like CPython 3.14; everywhere it does not, the divergence is written down.
This page gives you the shape of the subset. The other pages in this section are the exhaustive record, one per builtin, module or construct. They list every known divergence, including the ones that feel obvious, so a behaviour missing from them can be assumed to match CPython 3.14. They exist for development and for agents debugging code that runs on Monty; most users need only this page.
!!! tip
Turn on [type checking](../type-checking.md) rather than memorising this page.
Unsupported APIs generally fail before they run; see [the caveats](../type-checking.md#caveats) for the exceptions.
Supported:
def, async def, nested functions, closures, lambda__init__, __repr__/__str__, __eq__/__hash__, __iter__/__next__,
__contains__, __index__, class variables@dataclass, with the eq= and frozen= options only (every other option raises NotImplementedError, and
there is no field(), fields() or asdict()), plus host class instances passed in and out (and host classes the
sandbox may instantiate when granted)try / except / else / finally, raise ... from ...for, while, if / elif / else, break, continue, pass, assert, global, nonlocal, returnwith statements, for files and for classes implementing __enter__ / __exit__= debug form) and str.format(), with !r / !s / !a conversions, format specs and
nested replacement fieldsasync / await, and asyncio.run / asyncio.gatherimport x, import x.y, from x import y, z as wRejected at parse time, with NotImplementedError before any code runs:
class Foo(Bar):)@classmethod, @staticmethod, @propertyyield / yield from — there are no generator functions.
Generator expressions parse, but currently materialise to a listmatch statementsdel, both del x and del d[k]try* / except* exception groupstype aliasesasync with, async for and async comprehensionsfrom m import *)1j) and t-stringsMissing in other ways:
fn.__name__, fn.__doc__ and friends raise AttributeError, and new attributes cannot be set — so
functools.wraps-style metadata copying and registries keyed on fn.__name__ have no equivalent.eval, exec, compile, globals, locals, __import__ and super — all raise NameError.sys.path and no site-packages.The following modules are present:
| Module | Divergences |
|---|---|
asyncio | asyncio.md |
base64 | base64.md |
binascii | base64.md |
collections | collections.md |
dataclasses | dataclasses.md |
datetime | datetime.md |
functools | functools.md |
itertools | itertools.md |
json | json.md |
math | math.md |
os | os.md |
pathlib | pathlib.md |
re | re.md |
sys | sys.md |
typing | typing.md |
unicodedata | unicodedata.md |
Each covers only part of its CPython surface — often a small part.
The absent names are missing from the module namespace rather than stubbed, so they fail type checking as well as
raising AttributeError at runtime.
Notably absent: enum, contextlib, random, time, io, copy, string, struct, operator,
inspect, logging, traceback, hashlib, uuid, urllib.
Some of those are absent by design — socket, subprocess, multiprocessing, threading and ctypes would breach
the sandbox — and others are simply not implemented yet.
The authoritative list is modules.md.
A few divergences are worth knowing up front because they change how code behaves rather than whether it runs. Each links to the page that owns it, which is where the full account lives:
assert failures get pytest-style messages. assert 2 == 5 raises AssertionError: assert 2 == 5, not CPython's
empty AssertionError.
Turn it off with assert_message_annotations=False on checkout()
(assert.md).enumerate, zip, map, filter and reversed are eager, not lazy.
So map(f, itertools.count()) runs until a resource limit trips
(builtins.md).re is backed by Rust's fancy-regex, not CPython's engine: no bytes patterns, no VERBOSE flag, and some
error messages differ (re.md).__lt__, __len__, __getitem__, __call__ and the
arithmetic dunders raise TypeError as if undefined, while __bool__ and the __getattr__ family are ignored
silently, so an instance is always truthy (classes.md).async / await work, and asyncio exposes exactly two functions:
run and gather, the latter running host calls concurrently.
create_task, sleep and everything else do not exist
(asyncio.md).format() builtin and %-formatting are not implemented. Use str.format() or
f-strings (format.md).latin-1 and friends raise LookupError
(encoding.md).For a specific feature, open the page in this section named after the builtin, module or construct.
The pages are the limitations/ directory of the repository, published
verbatim.
If you hit something that is neither in the subset nor on these pages, open an
issue.