Skip to content

Commit

Permalink
bugfix for dataclass in dict (#162)
Browse files Browse the repository at this point in the history
* bugfix for dataclass in dict

* bump dev dependencies
  • Loading branch information
rnag authored Dec 4, 2024
1 parent 1ec1608 commit 128d585
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 7 deletions.
10 changes: 10 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
History
=======

0.32.1 (2024-12-04)
-------------------

**Bugfixes**

- Corrected logic in :class:`MappingParser` that assumed all parsers were
subclasses of :class:`AbstractParser` (:issue:`159`).
- Add test case to confirm intended functionality.
- Bump *dev* dependencies to latest version.

0.32.0 (2024-11-30)
-------------------

Expand Down
8 changes: 5 additions & 3 deletions dataclass_wizard/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ class MappingParser(AbstractParser[Type[M], M]):
__slots__ = ('hook',
'key_parser',
'val_parser',
'val_base_type')
'val_type')

base_type: Type[M]
hook: Callable[[Any, Type[M], AbstractParser, AbstractParser], M]
Expand All @@ -551,12 +551,12 @@ def __post_init__(self, cls: Type,
# Base type of the object which is instantiable
# ex. `Dict[str, Any]` -> `dict`
self.base_type: Type[M] = get_origin(self.base_type)
self.val_type = val_type

val_parser = get_parser(val_type, cls, extras)

self.key_parser = getattr(p := get_parser(key_type, cls, extras), '__call__', p)
self.val_parser = getattr(val_parser, '__call__', val_parser)
self.val_base_type = val_parser.base_type

def __call__(self, o: M) -> M:
return self.hook(o, self.base_type, self.key_parser, self.val_parser)
Expand All @@ -577,7 +577,9 @@ def __post_init__(self, cls: Type,
super().__post_init__(cls, extras, get_parser)

# The default factory argument to pass to the `defaultdict` subclass
self.default_factory: DefFactory = self.val_base_type
val_type = self.val_type
val_base_type = getattr(val_type, '__origin__', val_type)
self.default_factory: DefFactory = val_base_type

def __call__(self, o: DD) -> DD:
return self.hook(o, self.base_type, self.default_factory,
Expand Down
6 changes: 3 additions & 3 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pip>=21.3.1
bump2version==1.0.1
wheel==0.45.1
watchdog[watchmedo]==6.0.0
Sphinx==7.4.7; python_version == "3.9"
Sphinx==7.4.7; python_version == "3.9" # pyup: ignore
Sphinx==8.1.3; python_version >= "3.10"
twine==5.1.1
dataclass-wizard[toml]
twine==6.0.1
dataclass-wizard[toml] # pyup: ignore
2 changes: 1 addition & 1 deletion requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pytest==8.3.3
pytest==8.3.4
pytest-mock>=3.6.1
pytest-cov==6.0.0
# pytest-runner==5.3.1
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/test_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,3 +486,25 @@ class MyClass(JSONSerializable):
with expectation:
result = c.to_dict()
log.debug('Parsed object: %r', result)


def test_using_dataclass_in_dict():
"""
Using dataclass in a dictionary (i.e., dict[str, Test])
works as expected.
See https://github.com/rnag/dataclass-wizard/issues/159
"""
@dataclass
class Test:
field: str

@dataclass
class Config:
tests: dict[str, Test]

config = {"tests": {"test_a": {"field": "a"}, "test_b": {"field": "b"}}}

assert fromdict(Config, config) == Config(
tests={'test_a': Test(field='a'),
'test_b': Test(field='b')})

0 comments on commit 128d585

Please sign in to comment.