Skip to content

feat(validate): accept dataclass instances as input - #118

Merged
Oaklight merged 3 commits into
masterfrom
feature/validate-dataclass-instance
Jul 16, 2026
Merged

feat(validate): accept dataclass instances as input#118
Oaklight merged 3 commits into
masterfrom
feature/validate-dataclass-instance

Conversation

@Oaklight

@Oaklight Oaklight commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

  • validate() now accepts dataclass instances directly — no need to manually convert to dict first
  • Instances are auto-converted via dataclasses.fields() + getattr() at each nesting level, so nested dataclass trees are validated recursively
  • Cross-type validation works: a dataclass instance can be validated against a TypedDict schema (and vice versa)
  • Works with both regular and slots=True dataclasses
  • Previously, passing a dataclass instance would raise ValidationError: Expected dict, got <ClassName>

Implementation

2-line change in _validate_struct_fields: check if value is a dataclass instance, convert via {f.name: getattr(value, f.name) for f in dataclasses.fields(value)} before the existing isinstance(value, dict) guard. Each nesting level does its own lazy conversion. This approach:

  • Returns a fresh dict (no __dict__ aliasing — safe to mutate)
  • Works with slots=True dataclasses (no __dict__ needed)
  • Only surfaces declared fields (excludes post-init artifacts)

Test plan

  • 10 new tests: flat/nested instance validation, invalid field detection, cross-schema (DC→TypedDict), list-of-DC, default fields, slots=True, no-mutation safety
  • All 129 existing tests pass (no regressions)
  • All 19 benchmark tests pass
  • Pre-commit hooks (ruff, ty, complexipy) pass

Docs

Bilingual docs updated in docs_en/docs_zh branches:

  • Module docs: new "Dataclass Instance Validation" section with examples
  • API reference: updated data parameter description
  • Changelog: added entry under [Unreleased]

Oaklight added 2 commits July 16, 2026 10:04
Previously validate() required dict input even when the schema type was
a dataclass. Now dataclass instances are auto-converted via vars() at
each nesting level, enabling recursive validation of nested dataclass
trees and cross-type validation (DC instance against TypedDict schema).
…e conversion

Addresses review feedback:
1. vars() returns __dict__ reference — mutating the returned dict would
   alter the original instance. Now builds a fresh dict via
   {f.name: getattr(value, f.name) for f in dataclasses.fields(value)}.
2. dataclass(slots=True) removes __dict__, causing vars() to raise
   TypeError. dataclasses.fields() + getattr() works with both regular
   and slotted dataclasses.

Adds tests for slots=True support and mutation safety.

@Oaklight Oaklight left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review feedback addressed:

  1. __dict__ aliasing — switched from vars() to {f.name: getattr(value, f.name) for f in dataclasses.fields(value)}, returns a fresh dict that won't mutate the original instance.
  2. slots=True supportdataclasses.fields() + getattr() works regardless of whether __dict__ exists.

Both issues covered by new tests (test_instance_slots, test_instance_no_mutation). All 129 tests pass, pre-commit clean. Bilingual docs updated with corrected implementation details and pushed to docs_en/docs_zh branches.

@Oaklight
Oaklight merged commit 569a3a0 into master Jul 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant