From 778bf09c13c6b89ba9db93c711cff3eb313036fd Mon Sep 17 00:00:00 2001 From: sanjibani Date: Wed, 1 Apr 2026 17:17:12 +0530 Subject: [PATCH] fix: respect default_factory for abstract generic fields in from_dict When a field typed as an abstract generic (e.g. MutableMapping[str, str]) has a default_factory, calling from_dict({}) previously raised TypeError: MutableMapping() takes no arguments. Track fields populated by default_factory and skip _decode_generic for them. Fixes #505 Co-Authored-By: Claude Sonnet 4.6 --- dataclasses_json/core.py | 11 ++++++++--- tests/test_collections.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/dataclasses_json/core.py b/dataclasses_json/core.py index 69f51a3a..7b056f61 100644 --- a/dataclasses_json/core.py +++ b/dataclasses_json/core.py @@ -162,12 +162,14 @@ def _decode_dataclass(cls, kvs, infer_missing): decode_names = _decode_letter_case_overrides(field_names, overrides) kvs = {decode_names.get(k, k): v for k, v in kvs.items()} missing_fields = {field for field in fields(cls) if field.name not in kvs} + factory_populated_fields = set() for field in missing_fields: if field.default is not MISSING: kvs[field.name] = field.default elif field.default_factory is not MISSING: kvs[field.name] = field.default_factory() + factory_populated_fields.add(field.name) elif infer_missing: kvs[field.name] = None @@ -230,9 +232,12 @@ def _decode_dataclass(cls, kvs, infer_missing): infer_missing) init_kwargs[field.name] = value elif _is_supported_generic(field_type) and field_type != str: - init_kwargs[field.name] = _decode_generic(field_type, - field_value, - infer_missing) + if field.name in factory_populated_fields: + init_kwargs[field.name] = field_value + else: + init_kwargs[field.name] = _decode_generic(field_type, + field_value, + infer_missing) else: init_kwargs[field.name] = _support_extended_types(field_type, field_value) diff --git a/tests/test_collections.py b/tests/test_collections.py index b53a965f..f90e55c6 100644 --- a/tests/test_collections.py +++ b/tests/test_collections.py @@ -264,3 +264,23 @@ def test_counter(self): ) def test_abstract_collections(self, json_string, expected_instance): assert type(expected_instance).from_json(json_string) == expected_instance + + def test_mutable_mapping_default_factory_from_empty_dict(self): + """Regression test for issue #505: + MutableMapping fields with default_factory should not raise TypeError + when deserializing from an input dict that omits the field.""" + from collections.abc import MutableMapping + from dataclasses import dataclass, field + from dataclasses_json import DataClassJsonMixin + + @dataclass + class MyClass(DataClassJsonMixin): + field1: MutableMapping[str, str] = field(default_factory=dict) + + # Should not raise TypeError: MutableMapping() takes no arguments + result = MyClass.from_dict({}) + assert result.field1 == {} + + # Full round-trip should also work + obj = MyClass(field1={"key": "value"}) + assert MyClass.from_dict(obj.to_dict()).field1 == {"key": "value"}