Back to Monty

Exceptions

limitations/exceptions.md

0.0.215.5 KB
Original Source

Exceptions

Monty implements a fixed set of exception classes, listed below. Sandboxed code cannot define new exception classes: the class statement exists (see classes.md) but classes cannot inherit, so there is no way to subclass BaseException/Exception. raise must therefore use one of these built-ins — raise MyClass() on a plain user class raises TypeError: exceptions must derive from BaseException, as in CPython.

Implemented exception classes

BaseException, Exception, SystemExit, KeyboardInterrupt, ArithmeticError, OverflowError, ZeroDivisionError, LookupError, IndexError, KeyError, RuntimeError, NotImplementedError, RecursionError, AttributeError, FrozenInstanceError, NameError, UnboundLocalError, ValueError, UnicodeDecodeError, UnicodeEncodeError, ImportError, ModuleNotFoundError, OSError, FileNotFoundError, FileExistsError, IsADirectoryError, NotADirectoryError, PermissionError, AssertionError, MemoryError, StopIteration, SyntaxError, TimeoutError, TypeError.

Module-specific: json.JSONDecodeError (subclass of ValueError), re.PatternError / re.error, io.UnsupportedOperation (catchable as both OSError and ValueError, matching CPython's dual parentage).

Exception classes NOT implemented

Warning and all its subclasses (DeprecationWarning, etc.), BufferError, EOFError, FloatingPointError, GeneratorExit, ConnectionError and subclasses (ConnectionAbortedError, ConnectionRefusedError, ConnectionResetError, BrokenPipeError), BlockingIOError, ChildProcessError, InterruptedError, ProcessLookupError, ReferenceError, StopAsyncIteration, SystemError, TabError, IndentationError, UnicodeError (parent), UnicodeTranslateError, EncodingWarning, EnvironmentError / IOError aliases, ExceptionGroup / BaseExceptionGroup (see language.md).

Constructor signature

All exception constructors accept zero or one string argument only. Multi-argument forms used in CPython (e.g. OSError(errno, strerror, filename), UnicodeDecodeError(encoding, obj, start, end, reason)) are not supported — passing more than one argument raises an internal error.

Attributes

  • exc.args — a tuple with 0 or 1 elements. Always a tuple, even when empty.
  • str(exc) — returns the single message string, or "" if none.
  • repr(exc)ClassName('message') matching CPython, except UnicodeDecodeError/UnicodeEncodeError: CPython reprs these from their real 5-field constructor (UnicodeDecodeError('ascii', b'\xff', 0, 1, 'ordinal not in range(128)')), which Monty doesn't track — Monty's repr() uses the generic single-message form instead.

Not implemented: __cause__, __context__, __suppress_context__, __traceback__, __notes__, add_note(). The raise X from Y syntax parses, but the from Y cause is silently dropped — chained tracebacks are not preserved across raise from.

Custom subclasses

User class definitions are supported, but classes cannot inherit (class Foo(Exception): raises NotImplementedError: ... class inheritance and metaclasses), so there is no way to create a new exception class inside the sandbox. Raising a plain user class instance (raise MyClass()) fails with TypeError: exceptions must derive from BaseException. Define custom exception types on the host side if needed, or use the built-in subclass that best fits.

Control flow in finally

break/continue/return inside a finally block follows CPython semantics (the finally body runs exactly once and a return/break/ continue that exits it discards any in-flight exception), but Monty does not emit CPython 3.14's PEP 765 SyntaxWarning for such statements — Monty has no warnings machinery.

Traceback behaviour

Tracebacks are formatted to match CPython, including the File "...", line N, in <function> lines and ~ caret markers (Monty uses ~ where CPython uses ^; the test harness normalizes between them). Frame names use <module> for top-level code.

Known caret divergences:

  • CPython suppresses carets on a frame whose location is exactly the call in a simple name = f(...) assignment or return f(...) statement (a noise heuristic in traceback._should_show_carets); Monty always draws carets for the frame's range.
  • For a frame whose location spans multiple lines (e.g. a caller frame covering a whole multi-line class statement), Monty renders the CPython-style source block — all lines when the range covers at most three, otherwise ...<N lines>... elision — but never draws caret markers under it, where CPython draws multi-line carets for partial-line ranges (e.g. a multi-line binary expression).

Monty never emits CPython's Did you mean: '...'? suggestions on NameError/AttributeError. Note this divergence is invisible to the test suite: scripts/run_traceback.py strips the suggestions from CPython's output before comparison, so traceback tests cannot catch it.

An exception raised inside a Python callable that native code invokes synchronously — the key=/predicate/function argument of map, filter, sorted/min/max, and a user-defined __iter__/__next__/__contains__/__repr__/__str__ — omits the calling frame from its traceback; the callee frame is present. CPython shows both. This is a limitation of the re-entrant call path (evaluate_function), which does not splice the host call site into the traceback. The exception type and message are unaffected.