python / python/cpython

dataclasses astuple and asdict crash on recursive dataclass structures / dont support deepcopy memo

Abierto
#94,345 5 comentarios 11 reacciones 1 asignado Ver en GitHub

@ericvsmith ya está trabajando en esto.

Desde el 27/6/2022.

stdlib topic-dataclasses type-bug
Lenguaje dominante
Python
Estrellas
77.2k
Forks
36k
Merge medio
1 d 9 h
PR fusionados (30 d)
558

Descripción

I don't see this mentioned anywhere and it seems a bit unusual, we can't use asdict() or astuple() with dataclasses that have cycles to each other. This is something that is handled by most other stdlib features such as deepcopy, dataclasses stringify, etc.

Example below

from __future__ import annotations

import dataclasses

# dataclasses support recursive structures

@dataclasses.dataclass
class A:
    b: list[B] = dataclasses.field(default_factory=list)

@dataclasses.dataclass
class B:
    a: A

# we can make a recursive structure
a1 = A()
b1 = B(a1)
a1.b.append(b1)

# stringify supports recursion
print(a1)
print(b1)


# asdict and astuple don't however, with no way to even work around
# it (like pass in a set to track already seen objects)

# recursion overflow
dataclasses.asdict(a1)

# same
dataclasses.astuple(a1)

output:

A(b=[B(a=...)])
B(a=A(b=[...]))
Traceback (most recent call last):
  File "/home/classic/dev/sqlalchemy/test4.py", line 29, in <module>
    dataclasses.asdict(a1)
  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1232, in asdict
    return _asdict_inner(obj, dict_factory)
  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1239, in _asdict_inner
    value = _asdict_inner(getattr(obj, f.name), dict_factory)
  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1267, in _asdict_inner
    return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1267, in <genexpr>
    return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1239, in _asdict_inner
    value = _asdict_inner(getattr(obj, f.name), dict_factory)
  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1239, in _asdict_inner

...

  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1236, in _asdict_inner
    if _is_dataclass_instance(obj):
  File "/opt/python-3.10.0/lib/python3.10/dataclasses.py", line 1201, in _is_dataclass_instance
    return hasattr(type(obj), _FIELDS)
RecursionError: maximum recursion depth exceeded while calling a Python object

there seems to be no workaround as these two methods are pretty simple and don't accept any arguments like "check_recursion", IIUC there is also no directive on field() that would change this either?

Linked PRs
  • gh-151904

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.