[New-bugs-announce] [issue45135] dataclasses.asdict() incorrectly calls __deepcopy__() on values.
Thomas Fischbacher
report at bugs.python.org
Wed Sep 8 05:18:41 EDT 2021
New submission from Thomas Fischbacher <tfish at google.com>:
This problem may also be the issue underlying some other dataclasses.asdict() bugs:
https://bugs.python.org/issue?%40columns=id%2Cactivity%2Ctitle%2Ccreator%2Cassignee%2Cstatus%2Ctype&%40sort=-activity&%40filter=status&%40action=searchid&ignore=file%3Acontent&%40search_text=dataclasses.asdict&submit=search&status=-1%2C1%2C2%2C3
The documentation of dataclasses.asdict() states:
https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict
===
Converts the dataclass instance 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. For example: (...)
===
Given this documentation, the expectation about behavior is roughly:
def _dataclasses_asdict_equivalent_helper(obj, dict_factory=dict):
rec = lambda x: (
_dataclasses_asdict_equivalent_helper(x,
dict_factory=dict_factory))
if isinstance(obj, (list, tuple)):
return type(obj)(rec(x) for x in obj)
elif isinstance(obj, dict):
return type(obj)((k, rec(v) for k, v in obj.items())
# Otherwise, we are looking at a dataclass-instance.
for field in type(obj).__dataclass_fields__:
val = obj.__getattribute__[field]
if (hasattr(type(obj), '__dataclass_fields__')):
# ^ approx check for "is this a dataclass instance"?
# Not 100% correct. For illustration only.
ret[field] = rec(val)
ret[field] = val
return ret
def dataclasses_asdict_equivalent(x, dict_factory=dict):
if not hasattr(type(x), '__dataclass_fields__'):
raise ValueError(f'Not a dataclass: {x!r}')
return _dataclasses_asdict_equivalent(x, dict_factory=dict_factory)
In particular, field-values that are neither dict, list, tuple, or dataclass-instances are expected to be used identically.
What actually happens however is that .asdict() DOES call __deepcopy__ on field values it has no business inspecting:
===
import dataclasses
@dataclasses.dataclass
class Demo:
field_a: object
class Obj:
def __init__(self, x):
self._x = x
def __deepcopy__(self, *args):
raise ValueError('BOOM!')
###
d1 = Demo(field_a=Obj([1,2,3]))
dd = dataclasses.asdict(d1)
# ...Execution does run into a "BOOM!" ValueError.
===
Apart from this: It would be very useful if dataclasses.asdict() came with a recurse={boolish} parameter with which one can turn off recursive translation of value-objects.
----------
components: Library (Lib)
messages: 401360
nosy: tfish2
priority: normal
severity: normal
status: open
title: dataclasses.asdict() incorrectly calls __deepcopy__() on values.
type: behavior
_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue45135>
_______________________________________
More information about the New-bugs-announce
mailing list