Skip to content

v0.4.0

Choose a tag to compare

@aaronstevenwhite aaronstevenwhite released this 05 May 18:30
· 17 commits to main since this release
91d983f

Added

  • ModelConfig.extra="ignore" is honoured: keyword arguments at construction (and dict keys at model_validate) that don't match a declared field are silently dropped. with_() stays strict regardless; an unknown kwarg there is always a programming error. (#11)
  • Generic Models auto-parameterise on subscript. Both PEP 695 syntax (class Range[T: int | float](dx.Model): ...) and the legacy Generic[T] mixin form work. Range[int](min=0, max=10) returns an instance of a synthesised concrete subclass; the subclass is cached per type-arg tuple on the generic parent so repeated subscripts return the same class object and its Theory is built once. Substitution walks through nested generic shapes: tuple[T, ...], dict[str, T], T | None, Annotated[T, *meta], Embed[T], Ref[T], and unions of these are all rewritten correctly. Class-level defaults (min: T = 0) and dx.field(...) metadata (default, default_factory, description, alias, examples, deprecated, nominal, usage_mode, extras, converter) propagate from the generic parent onto the synthesised subclass. (#12)
  • read_class_annotations is part of the public surface (lifted from the underscore-prefixed _read_class_annotations) and the metaclass's annotation-reader return type is dict[str, type | TypeVar | ForwardRef] to reflect what the PEP 695 generic-parameter path produces.

Fixed

  • Inherited field defaults survive on subclass. Child(Base) where Base declares id: str = "default-id" constructs cleanly with the inherited default. ModelMeta.collect_field_specs walks ancestor classes by copying their already-finalised FieldSpec; it only re-runs _build_field_spec for the target class's own annotations. (Reading __dict__ for ancestor classes lost their defaults because the metaclass strips field defaults from the class dict at the end of each Model's class-creation step.) (#13)
  • The deferred-TypeVar branch in _build_field_spec carries through every Field attribute (default, default_factory, converter, alias, description, examples, deprecated, nominal, usage_mode, extras), so a generic with value: T = dx.field(default=42, description="...") keeps that metadata available for parameterisation.

Removed

  • The leftover tracking-comment blocks from the v0.3.2 suppression unwind are stripped from every file in the workspace. They documented suppressions that no longer exist.

Full changelog: https://github.com/panproto/didactic/blob/main/CHANGELOG.md