dataclasses module
Native, in-sandbox @dataclass: sandboxed code can define its own dataclasses,
executed entirely inside the sandbox (unlike host-supplied class instances,
which enter via the ClassInstance wrapper and dispatch back to the host —
see classes.md).
Host-supplied instances and this module barely interact:
dataclasses.is_dataclass(x) honours the flag the host sent, but fields()
and asdict() do not work on host instances (they are not native
dataclasses). Bare dataclasses are NOT accepted as inputs — the host must
wrap them in ClassInstance explicitly.
@dataclass, @dataclass(...) with eq and/or frozen, field() with
default/default_factory, MISSING, __post_init__ and is_dataclass
exist. Each unsupported feature listed below raises where it is written —
at the decoration, or at the field() call — rather than producing a subtly
wrong class, so a class body Monty cannot honour never silently misbehaves.
Each raises NotImplementedError, marking a feature Monty has not built yet
rather than a mistake in the calling code. CPython accepts all of them, so the
exception type is a divergence in its own right: code catching TypeError
around a decoration will not catch these.
- Every
@dataclass(...)option excepteqandfrozen—init,repr,order,unsafe_hash,match_args,kw_only,slotsandweakref_slot. Setting one away from its CPython default raisesNotImplementedError: dataclass() does not yet support the <name> option; each is named individually rather than reported as an unknown keyword. The defaultmatch_args=Truedoes write__match_args__(every field, in order) unless the class body defines its own, so positional class patterns work; see match.md. Ordering dunders therefore do not exist, and hashing is whatevereq/frozenimply. InitVar[...]— raisesNotImplementedError: dataclass() does not yet support InitVar (field <name>), which would become an ordinary field. Matched by name in a string or unresolved (ForwardRef) annotation, Monty having noInitVarobject: the name need not be imported to be rejected.- Every
field(...)argument exceptdefaultanddefault_factory—init,repr,hash,compare,metadata,kw_onlyanddoc. Setting one away from its CPython default raisesNotImplementedError: field() does not yet support the <name> argument, at thefield()call rather than at decoration. Nothing consults the three flags when the dunders are synthesized, soinit=Falsewould otherwise leave the field in__init__regardless, and Monty stores no per-field docstring fordocto fill. They therefore always read back as CPython’s defaults (f.init is True,f.kw_only is False,f.doc is None). Field.metadataandField._field_type— raiseNotImplementedError: Field.metadata is not yet supported, types.MappingProxyType is not implemented(and likewisedataclasses._FIELD), the objects behind them being unimplemented.- Module helpers —
fields,asdict,astupleandreplacedo not exist: accessing them raisesAttributeError, notNotImplementedError, since the module has no such attribute. __replace__is never generated, soC.__replace__raisesAttributeErrorwhere CPython gives a method;copy.replaceis covered in copy.md.
Mutable defaults are rejected as CPython rejects them
(ValueError: mutable default <class 'list'> for field xs is not allowed: use default_factory), and so is a non-default
field after a defaulted one
(TypeError: non-default argument 'b' follows default argument 'a').
- Annotations are evaluated at decoration, synchronously.
@dataclassreadsannotationlib.get_annotations(cls, format=Format.FORWARDREF)as CPython does, soField.typeis the evaluated object, aForwardReffor a name not yet defined, or the string afrom __future__ import annotationsclass stores (see typing.md). The evaluation runs in the synchronous position ofdefault_factorybelow, so an annotation that calls an external function raisesNotImplementedErrorwhere CPython evaluates it. - An annotation key that is not a string literal is skipped. A
type()namespace whose__annotations__has a key such as1is decorated without it, where CPython raisesTypeError: attribute name must be string, not 'int'; a key built at run time ('a' + b) is skipped too, where CPython makes it a field. __dataclass_fields__holds only real fields. CPython keepsClassVar(andInitVar) entries in the mapping, marked_FIELD_CLASSVAR, and filters them infields(). Monty has no field kinds, so the mapping is the field list and class variables never appear in it.Fieldrenders differently.repr(field)follows CPython’s layout, butrepr(type(field))is<class 'Field'>, not<class 'dataclasses.Field'>(Field.__name__matches either way, so attribute errors read the same).- Error messages name
MISSING’s type with its module.MISSING | intandMISSING.foosay'dataclasses._MISSING_TYPE'where CPython says'_MISSING_TYPE'.type(MISSING)and__name__match. type(MISSING)()raisesTypeError: cannot create 'dataclasses._MISSING_TYPE' instances, where CPython builds a second, distinct object.default_factoryand__post_init__cannot suspend. Both run in a synchronous position the interpreter cannot preserve and resume, so calling an external function, anosfunction, or awaiting inside one raisesNotImplementedError: external function 'f' cannot suspend in this synchronous context. The traceback identifies the calling code. Ordinary in-sandbox code in them runs normally.- A string or unresolved
ClassVar/InitVaris matched by text. AClassVarobject orClassVar[...]alias is recognised as in CPython. A string orForwardRefannotation is matched by its text (bare, dotted, subscripted or quoted) without checking that the name is imported, where CPython resolves a string annotation through the defining module’s namespace. Soc: "ClassVar[int]"withoutClassVarin scope is excluded by Monty but is an ordinary field to CPython; any dotted spelling matches, so a same-named attribute on an unrelated module (mymod.ClassVar) is treated astyping.ClassVar; and an undefinedInitVaris rejected where CPython would make the field ordinary. - A field holding a function or bound method reprs differently, since
Monty’s own
reprfor those differs (see classes.md). Only the text differs; the value and its equality match CPython. - The generated
__repr__uses the class’s__name__, since Monty classes have no__qualname__(see classes.md). A dataclass defined in a function reprs asLocal(x=1)where CPython givesmake.<locals>.Local(x=1). - A class-body
__setattr__never runs for the generated__init__of a non-frozen dataclass, which writes fields straight into the instance__dict__. This is the never-dispatched attribute hook described in classes.md rather than something dataclass-specific. @dataclasson a non-class (e.g.dataclasses.dataclass(5)) raisesTypeError: dataclass() should be called on a class, not '<type>'. CPython instead raises an incidentalAttributeErrorabout__module__from its implementation. The@decosyntax only ever targets a class, so this affects only direct calls.dataclass(...)returns a native callable, not a Python function. CPython builds a closure, which Monty cannot: a native function has nowhere to keep the bound options but its own value. Applying it to a class is identical, and it reprs as<function dataclass at 0x..>, buttype()saysbuiltin_function_or_methodwhere CPython saysfunction, and CPython’s repr names the closure (dataclass.<locals>.wrap). Having nowhere to live but the value, the options are the value:dataclass(frozen=True) is dataclass(frozen=True)isTrue, where each CPython call builds a fresh closure. Fixable only if Monty gains closures over native functions; nothing else depends on that, so it is not planned.del obj.fieldon a frozen instance never raisescannot delete field, because Monty’s parser has nodelstatement at all. Calling the generatedC.__delattr__(obj, 'x')directly raises it as CPython does, and assignment matches CPython, message included.__dataclass_params__reads back normalised.C.__dataclass_params__exists, reprs like CPython’s and answers all ten flags, but each is theboolMonty acted on:@dataclass(frozen=1)reportsfrozen=Truewhere CPython echoes the1you passed.- The generated methods follow Monty’s function conventions. Each method
@dataclassgenerates (__init__,__repr__,__eq__,__hash__, and__setattr__/__delattr__when frozen, as CPython’s options and the class body decide) reportsfunctionfromtype(), reprs as<function 'C.__init__' at 0x..>and lacks__name__, as every Monty function does (see language.md). - Calling a generated method directly needs an instance of a sandbox class
as
self.C.__repr__(5)raisesTypeError: C.__repr__() requires a class instance as self, not 'int', where CPython’s generated code reads the fields off whatever it is given and fails on the first one (AttributeError: 'int' object has no attribute 'x'). Argument binding and its error messages match CPython. - A frozen
__setattr__refuses every attribute on any instance. CPython’s refuses only whenselfis an instance of the decorated class or the name is a field, and otherwise defers tosuper(). SoF.__setattr__(other, 'w', 1), orQ.__setattr__ = F.__setattr__followed byQ().w = 1, raisesFrozenInstanceErrorwhere CPython raises aTypeErrorfromsuper().
- No inheritance, so field inheritance across base dataclasses is unsupported (Monty has no class inheritance at all).
slots=True/weakref_slot=True— no__slots__, no weakrefs.