Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Doc/library/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,8 @@ Module contents
Converts the dataclass *obj* to a dict (by using the
factory function *dict_factory*). Each dataclass is converted
to a dict of its fields, as ``name: value`` pairs. dataclasses, dicts,
lists, and tuples are recursed into. Other objects are copied with
:func:`copy.deepcopy`.
frozendicts, lists, and tuples are recursed into. Other objects are copied
with :func:`copy.deepcopy`.

Example of using :func:`!asdict` on nested dataclasses::

Expand Down Expand Up @@ -402,8 +402,8 @@ Module contents

Converts the dataclass *obj* to a tuple (by using the
factory function *tuple_factory*). Each dataclass is converted
to a tuple of its field values. dataclasses, dicts, lists, and
tuples are recursed into. Other objects are copied with
to a tuple of its field values. dataclasses, dicts, frozendicts, lists,
and tuples are recursed into. Other objects are copied with
:func:`copy.deepcopy`.

Continuing from the previous example::
Expand Down
4 changes: 2 additions & 2 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,7 @@ def _asdict_inner(obj, dict_factory):
return obj_type(*[_asdict_inner(v, dict_factory) for v in obj])
else:
return obj_type(_asdict_inner(v, dict_factory) for v in obj)
elif issubclass(obj_type, dict):
elif issubclass(obj_type, (dict, frozendict)):
if hasattr(obj_type, 'default_factory'):
# obj is a defaultdict, which has a different constructor from
# dict as it requires the default_factory as its first arg.
Expand Down Expand Up @@ -1610,7 +1610,7 @@ def _astuple_inner(obj, tuple_factory):
# generator (which is not true for namedtuples, handled
# above).
return type(obj)(_astuple_inner(v, tuple_factory) for v in obj)
elif isinstance(obj, dict):
elif isinstance(obj, (dict, frozendict)):
obj_type = type(obj)
if hasattr(obj_type, 'default_factory'):
# obj is a defaultdict, which has a different constructor from
Expand Down
20 changes: 17 additions & 3 deletions Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1693,17 +1693,24 @@ class GroupTuple:
class GroupDict:
id: int
users: Dict[str, User]
@dataclass
class GroupFrozenDict:
id: int
users: frozendict[str, User]
a = User('Alice', 1)
b = User('Bob', 2)
gl = GroupList(0, [a, b])
gt = GroupTuple(0, (a, b))
gd = GroupDict(0, {'first': a, 'second': b})
gfd = GroupFrozenDict(0, frozendict({'first': a, 'second': b}))
self.assertEqual(asdict(gl), {'id': 0, 'users': [{'name': 'Alice', 'id': 1},
{'name': 'Bob', 'id': 2}]})
self.assertEqual(asdict(gt), {'id': 0, 'users': ({'name': 'Alice', 'id': 1},
{'name': 'Bob', 'id': 2})})
self.assertEqual(asdict(gd), {'id': 0, 'users': {'first': {'name': 'Alice', 'id': 1},
'second': {'name': 'Bob', 'id': 2}}})
expected_dict = {'id': 0, 'users': {'first': {'name': 'Alice', 'id': 1},
'second': {'name': 'Bob', 'id': 2}}}
self.assertEqual(asdict(gd), expected_dict)
self.assertEqual(asdict(gfd), expected_dict)

def test_helper_asdict_builtin_object_containers(self):
@dataclass
Expand Down Expand Up @@ -1884,14 +1891,21 @@ class GroupTuple:
class GroupDict:
id: int
users: Dict[str, User]
@dataclass
class GroupFrozenDict:
id: int
users: frozendict[str, User]
a = User('Alice', 1)
b = User('Bob', 2)
gl = GroupList(0, [a, b])
gt = GroupTuple(0, (a, b))
gd = GroupDict(0, {'first': a, 'second': b})
gfd = GroupFrozenDict(0, frozendict({'first': a, 'second': b}))
self.assertEqual(astuple(gl), (0, [('Alice', 1), ('Bob', 2)]))
self.assertEqual(astuple(gt), (0, (('Alice', 1), ('Bob', 2))))
self.assertEqual(astuple(gd), (0, {'first': ('Alice', 1), 'second': ('Bob', 2)}))
d = {'first': ('Alice', 1), 'second': ('Bob', 2)}
self.assertEqual(astuple(gd), (0, d))
self.assertEqual(astuple(gfd), (0, frozendict(d)))

def test_helper_astuple_builtin_object_containers(self):
@dataclass
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for :class:`frozendict` in :meth:`dataclasses.asdict` and :meth:`dataclasses.astuple`.
Loading