Skip to content

Commit c46a423

Browse files
authored
bpo-35540 dataclasses.asdict now supports defaultdict fields (gh-32056)
1 parent a4b7794 commit c46a423

File tree

3 files changed

+28
-2
lines changed

3 files changed

+28
-2
lines changed

Lib/dataclasses.py

+8
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,14 @@ def _asdict_inner(obj, dict_factory):
13251325
# generator (which is not true for namedtuples, handled
13261326
# above).
13271327
return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
1328+
elif isinstance(obj, dict) and hasattr(type(obj), 'default_factory'):
1329+
# obj is a defaultdict, which has a different constructor from
1330+
# dict as it requires the default_factory as its first arg.
1331+
# https://bugs.python.org/issue35540
1332+
result = type(obj)(getattr(obj, 'default_factory'))
1333+
for k, v in obj.items():
1334+
result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory)
1335+
return result
13281336
elif isinstance(obj, dict):
13291337
return type(obj)((_asdict_inner(k, dict_factory),
13301338
_asdict_inner(v, dict_factory))

Lib/test/test_dataclasses.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
import weakref
1313
import unittest
1414
from unittest.mock import Mock
15-
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol
15+
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict
1616
from typing import get_type_hints
17-
from collections import deque, OrderedDict, namedtuple
17+
from collections import deque, OrderedDict, namedtuple, defaultdict
1818
from functools import total_ordering
1919

2020
import typing # Needed for the string "typing.ClassVar[int]" to work as an annotation.
@@ -1677,6 +1677,23 @@ class C:
16771677
self.assertIsNot(d['f'], t)
16781678
self.assertEqual(d['f'].my_a(), 6)
16791679

1680+
def test_helper_asdict_defaultdict(self):
1681+
# Ensure asdict() does not throw exceptions when a
1682+
# defaultdict is a member of a dataclass
1683+
1684+
@dataclass
1685+
class C:
1686+
mp: DefaultDict[str, List]
1687+
1688+
1689+
dd = defaultdict(list)
1690+
dd["x"].append(12)
1691+
c = C(mp=dd)
1692+
d = asdict(c)
1693+
1694+
assert d == {"mp": {"x": [12]}}
1695+
assert d["mp"] is not c.mp # make sure defaultdict is copied
1696+
16801697
def test_helper_astuple(self):
16811698
# Basic tests for astuple(), it should return a new tuple.
16821699
@dataclass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :func:`dataclasses.asdict` crash when :class:`collections.defaultdict` is present in the attributes.

0 commit comments

Comments
 (0)