fix: don't override __post_init__ for non-init fields absent from data - #301
Open
gaoflow wants to merge 1 commit into
Open
fix: don't override __post_init__ for non-init fields absent from data#301gaoflow wants to merge 1 commit into
gaoflow wants to merge 1 commit into
Conversation
When a dataclass field is declared with `field(init=False)` and carries an explicit `default=` or `default_factory=`, `from_dict` would incorrectly push the stored default value into `post_init_values` and then call `setattr` on the freshly constructed instance — overwriting whatever `__post_init__` had computed from the other fields. Root cause: the `else` branch (key absent from the input dict) called `get_default_value_for_field`, stored the result in `post_init_values`, and applied it after construction regardless of whether the field is managed by `__post_init__`. Fix: when the key is absent AND the field has `init=False` AND the field carries an explicit `default` or `default_factory`, skip the field entirely. Fields with `init=False` but no explicit default (e.g. `Optional[str]` that relies on dacite's `is_optional → None` fallback) continue to receive the inferred default, preserving existing behaviour. Fixes konradhalas#244, konradhalas#285
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
When a dataclass field is declared
field(init=False)with an explicitdefault=ordefault_factory=,from_dictoverwrites whatever__post_init__computed by callingsetattrwith the stored defaultafter construction — producing a result that differs from a direct
DataClass(**data)call.Same bug with
default=:Fixes #244, #285.
Root cause
_from_dictiterates over all dataclass fields. When a key is absent from theinput dict and the field has
init=False,get_default_value_for_fieldreturnsthe default value; that value is then placed in
post_init_valuesand appliedwith
setattrafter__post_init__has already run.Fix
Add an early
continuein theelsebranch for the absent-key case: whenfield.init is Falseand the field carries an explicitdefaultordefault_factory, skip it entirely and let__post_init__manage the value.Fields with
init=Falsebut no explicit default (e.g.Optional[str]thatrelies on dacite's
is_optional → Nonefallback) retain their existingbehaviour.
Tests
Two new test cases cover the
defaultanddefault_factoryvariants; all 205tests pass.