Include inherited fields in slotted dataclass formatting

RU
2d ago
+206-4

Formatting a fully slotted dataclass subclass only enumerates its leaf __slots__, so inherited dataclass fields disappear. A child with no new slots can print as empty despite holding inherited values.

Add missing dataclass field names to the existing no-__dict__ fallback, then retain the leaf slots in their original order. This preserves manually declared auxiliary slots, explicit weakref slots and existing ordering; string-form slot metadata is treated as one name. Classes with an instance __dict__ keep their current behavior. Genuinely absent slot attributes are omitted, so inherited field(init=False, repr=False) values are safe before assignment. Assigned values, including the formatter’s own MISSING object, remain visible.

Validation on CPython 3.13.14 / macOS arm64:

  • The final 53 formatter cases reproduce five failures on pristine source and all pass with this change.
  • Complete suites pass with extras (162 passed, four existing skips) and without extras (155 passed, 11 existing skips).
  • Ruff, strict basedpyright and strict docs pass.
  • Public API controls cover multilevel inheritance, overridden fields, empty child slots, manual slot order/auxiliary values, InitVar/ClassVar, dynamic attributes and the unchanged hybrid-class path.

Other supported runtime/OS combinations remain unverified locally. This only changes the existing fully slotted fallback; it does not add previously hidden inherited non-field auxiliary slots.


Summary by cubic

Fixes formatting of fully slotted dataclass subclasses so inherited fields no longer disappear from pformat() output.

Bug Fixes

  • Adds dataclass field names missing from the leaf __slots__ to the no-__dict__ fallback, matching dataclass field ordering.
  • Skips slots whose attributes are genuinely unset instead of raising AttributeError.
  • Preserves manually declared auxiliary slots and explicit weakref slots; string-form slot metadata is treated as one name.
  • Classes with an instance __dict__ keep their current behavior.