|
9 | 9 | # obtain one at https://mozilla.org/MPL/2.0/.
|
10 | 10 |
|
11 | 11 | import codecs
|
| 12 | +import copy |
| 13 | +import dataclasses |
12 | 14 | import inspect
|
13 | 15 | import platform
|
14 | 16 | import sys
|
@@ -188,3 +190,46 @@ def bad_django_TestCase(runner):
|
188 | 190 | from hypothesis.extra.django._impl import HypothesisTestCase
|
189 | 191 |
|
190 | 192 | return not isinstance(runner, HypothesisTestCase)
|
| 193 | + |
| 194 | + |
| 195 | +# see issue #3812 |
| 196 | +if sys.version_info[:2] < (3, 12): |
| 197 | + |
| 198 | + def dataclass_asdict(obj, *, dict_factory=dict): |
| 199 | + """ |
| 200 | + A vendored variant of dataclasses.asdict. Includes the bugfix for |
| 201 | + defaultdicts (cpython/32056) for all versions. See also issues/3812. |
| 202 | +
|
| 203 | + This should be removed whenever we drop support for 3.11. We can use the |
| 204 | + standard dataclasses.asdict after that point. |
| 205 | + """ |
| 206 | + if not dataclasses._is_dataclass_instance(obj): # pragma: no cover |
| 207 | + raise TypeError("asdict() should be called on dataclass instances") |
| 208 | + return _asdict_inner(obj, dict_factory) |
| 209 | + |
| 210 | +else: # pragma: no cover |
| 211 | + dataclass_asdict = dataclasses.asdict |
| 212 | + |
| 213 | + |
| 214 | +def _asdict_inner(obj, dict_factory): |
| 215 | + if dataclasses._is_dataclass_instance(obj): |
| 216 | + return dict_factory( |
| 217 | + (f.name, _asdict_inner(getattr(obj, f.name), dict_factory)) |
| 218 | + for f in dataclasses.fields(obj) |
| 219 | + ) |
| 220 | + elif isinstance(obj, tuple) and hasattr(obj, "_fields"): |
| 221 | + return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj]) |
| 222 | + elif isinstance(obj, (list, tuple)): |
| 223 | + return type(obj)(_asdict_inner(v, dict_factory) for v in obj) |
| 224 | + elif isinstance(obj, dict): |
| 225 | + if hasattr(type(obj), "default_factory"): |
| 226 | + result = type(obj)(obj.default_factory) |
| 227 | + for k, v in obj.items(): |
| 228 | + result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory) |
| 229 | + return result |
| 230 | + return type(obj)( |
| 231 | + (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) |
| 232 | + for k, v in obj.items() |
| 233 | + ) |
| 234 | + else: |
| 235 | + return copy.deepcopy(obj) |
0 commit comments