Back to Monty

`dataclasses` module

limitations/dataclasses.md

0.0.215.3 KB
Original Source

dataclasses module

Native, in-sandbox @dataclass: sandboxed code can define its own dataclasses, executed entirely inside the sandbox (unlike host-supplied dataclasses, which are passed in and dispatch back to the host — see classes.md).

Unsupported

Only the bare @dataclass decorator and is_dataclass exist. Everything below raises at decoration time rather than producing a subtly wrong class, so a class body Monty cannot honour never silently misbehaves.

Each raises NotImplementedError — a feature Monty has not built yet, not 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.

  • The @dataclass(...) keyword formfrozen, eq=False, order, unsafe_hash, kw_only and the hashing/ordering they imply. Any keyword raises NotImplementedError: dataclass() keyword options (eq, order, frozen, unsafe_hash, ...) are not yet supported. A dataclass is therefore always eq=True, frozen=False: instances are unhashable and unordered.
  • __post_init__ — raises NotImplementedError: dataclass() does not yet support __post_init__ in a class body, which would be silently skipped.
  • InitVar[...] — raises NotImplementedError: dataclass() does not yet support InitVar (field <name>), which would become an ordinary field. Detected textually, since annotations are never evaluated: the name need not be imported to be rejected.
  • field() / default_factory / MISSINGfield(...) in a class body raises NameError. There is no MISSING object, so the Field attributes whose value would be one raise NotImplementedError: Field.default is not yet supported, dataclasses.MISSING is not implemented (likewise default_factory, and default only for a field that has none). Field.metadata and Field._field_type raise the same way, for types.MappingProxyType and dataclasses._FIELD.
  • Module helpersfields, asdict, astuple, replace.

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').

Divergences from CPython

  • Annotations are stringized. Fields come from the class's __annotations__, which Monty stores as never-evaluated source text (always PEP 563) — see typing.md. Field discovery and the generated methods are unaffected, the field type being inert metadata, but C.__dataclass_fields__['x'].type is the string 'int', not the int type object.
  • __dataclass_fields__ holds only real fields. CPython keeps ClassVar (and InitVar) entries in the mapping, marked _FIELD_CLASSVAR, and filters them in fields(). Monty has no field kinds — the mapping is the field list — so class variables never appear in it.
  • Field renders differently. repr(field) follows CPython's layout but writes MISSING where CPython writes <dataclasses._MISSING_TYPE object at 0x..>, and the stringized type. repr(type(field)) is <class 'Field'>, not <class 'dataclasses.Field'> (Field.__name__ matches either way, so attribute errors read the same).
  • Overwriting __dataclass_fields__ un-marks the class. Every dunder reads the mapping from the class namespace, so C.__dataclass_fields__ = 5 makes is_dataclass(C) false and C(...) construct like a plain class. CPython keeps its generated methods and still calls C a dataclass.
  • ClassVar / InitVar detection is purely textual. Monty matches the annotation text (bare, dotted, subscripted, or quoted) without checking that the name is actually imported, where CPython resolves a string annotation through the defining module's namespace. So c: "ClassVar[int]" without ClassVar in scope is excluded by Monty but is an ordinary field to CPython. Conversely any dotted spelling matches, so a same-named attribute on an unrelated module (mymod.ClassVar) is treated as typing.ClassVar.
  • A field holding a function or bound method reprs differently, since Monty's own repr for those differs (see classes.md). Only the text differs; the value and its equality match CPython.
  • A class-body __setattr__ never runs for the synthesized __init__, which writes fields straight into the instance __dict__. Not a dataclass-specific gap — the never-dispatched attribute hook of classes.md — so @dataclass does not reject it.
  • @dataclass on a non-class (e.g. dataclasses.dataclass(5)) raises TypeError: dataclass() should be called on a class, not '<type>'. CPython instead raises an incidental AttributeError about __module__ from its implementation; Monty reports the misuse directly. (The @deco syntax only ever targets a class, so this affects only direct calls.)

Architectural gaps (cannot match)

  • 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.