mirror of
https://github.com/python/cpython.git
synced 2025-09-24 01:13:22 +00:00
bpo-35540 dataclasses.asdict now supports defaultdict fields (gh-32056)
This commit is contained in:
parent
a4b7794887
commit
c46a423a52
3 changed files with 28 additions and 2 deletions
|
@ -1325,6 +1325,14 @@ def _asdict_inner(obj, dict_factory):
|
||||||
# generator (which is not true for namedtuples, handled
|
# generator (which is not true for namedtuples, handled
|
||||||
# above).
|
# above).
|
||||||
return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
|
return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
|
||||||
|
elif isinstance(obj, dict) and hasattr(type(obj), 'default_factory'):
|
||||||
|
# obj is a defaultdict, which has a different constructor from
|
||||||
|
# dict as it requires the default_factory as its first arg.
|
||||||
|
# https://bugs.python.org/issue35540
|
||||||
|
result = type(obj)(getattr(obj, 'default_factory'))
|
||||||
|
for k, v in obj.items():
|
||||||
|
result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory)
|
||||||
|
return result
|
||||||
elif isinstance(obj, dict):
|
elif isinstance(obj, dict):
|
||||||
return type(obj)((_asdict_inner(k, dict_factory),
|
return type(obj)((_asdict_inner(k, dict_factory),
|
||||||
_asdict_inner(v, dict_factory))
|
_asdict_inner(v, dict_factory))
|
||||||
|
|
|
@ -12,9 +12,9 @@ import types
|
||||||
import weakref
|
import weakref
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol
|
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict
|
||||||
from typing import get_type_hints
|
from typing import get_type_hints
|
||||||
from collections import deque, OrderedDict, namedtuple
|
from collections import deque, OrderedDict, namedtuple, defaultdict
|
||||||
from functools import total_ordering
|
from functools import total_ordering
|
||||||
|
|
||||||
import typing # Needed for the string "typing.ClassVar[int]" to work as an annotation.
|
import typing # Needed for the string "typing.ClassVar[int]" to work as an annotation.
|
||||||
|
@ -1677,6 +1677,23 @@ class TestCase(unittest.TestCase):
|
||||||
self.assertIsNot(d['f'], t)
|
self.assertIsNot(d['f'], t)
|
||||||
self.assertEqual(d['f'].my_a(), 6)
|
self.assertEqual(d['f'].my_a(), 6)
|
||||||
|
|
||||||
|
def test_helper_asdict_defaultdict(self):
|
||||||
|
# Ensure asdict() does not throw exceptions when a
|
||||||
|
# defaultdict is a member of a dataclass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class C:
|
||||||
|
mp: DefaultDict[str, List]
|
||||||
|
|
||||||
|
|
||||||
|
dd = defaultdict(list)
|
||||||
|
dd["x"].append(12)
|
||||||
|
c = C(mp=dd)
|
||||||
|
d = asdict(c)
|
||||||
|
|
||||||
|
assert d == {"mp": {"x": [12]}}
|
||||||
|
assert d["mp"] is not c.mp # make sure defaultdict is copied
|
||||||
|
|
||||||
def test_helper_astuple(self):
|
def test_helper_astuple(self):
|
||||||
# Basic tests for astuple(), it should return a new tuple.
|
# Basic tests for astuple(), it should return a new tuple.
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix :func:`dataclasses.asdict` crash when :class:`collections.defaultdict` is present in the attributes.
|
Loading…
Add table
Add a link
Reference in a new issue