Dataclass default_factory field treated as required in union disambiguation
Los mantenedores suelen responder en 1 día
@Eryx-Z ya está trabajando en esto.
Desde el 16/10/2026.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 72/100
Línea de trabajo
Empieza ejecutando el script de reproducción del issue para confirmar que la unión de dataclass devuelve D2 para un payload que no tiene tags. La comprobación de valores por defecto está en create_default_dis_func en src/cattrs/disambiguators.py, alrededor de las líneas 154-157, y fields_dict y adapted_fields en src/cattrs/_compat.py muestran por qué default_factory es invisible ahí. Está terminado cuando el caso de dataclass coincide con el comportamiento de attrs, con un test de default_factory añadido en tests/test_disambiguators.py.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
A dataclass field with default_factory is treated as a required field when cattrs builds the disambiguator for a union of dataclasses, so a payload that omits it is silently structured as the other class.
Minimal example
from dataclasses import dataclass, field
from typing import Union
from attrs import Factory, define
from cattrs import Converter
@dataclass
class D1:
x: int
tags: list = field(default_factory=list)
@dataclass
class D2:
x: int
c = Converter(omit_if_default=True)
payload = c.unstructure(D1(1))
print("payload:", payload)
for union in (Union[D1, D2], Union[D2, D1]):
print(f"structure(payload, {union}) observed:", repr(c.structure(payload, union)))
print("expected: D1(x=1, tags=[]) or a 'Could not disambiguate' TypeError, as for attrs below")
@define
class A1:
x: int
tags: list = Factory(list)
@define
class A2:
x: int
c2 = Converter(omit_if_default=True)
try:
print("attrs twin:", repr(c2.structure(c2.unstructure(A1(1)), Union[A1, A2])))
except TypeError as e:
print("attrs twin raises:", type(e).__name__, e)
Observed output
payload: {'x': 1}
structure(payload, typing.Union[__main__.D1, __main__.D2]) observed: D2(x=1)
structure(payload, typing.Union[__main__.D2, __main__.D1]) observed: D2(x=1)
expected: D1(x=1, tags=[]) or a 'Could not disambiguate' TypeError, as for attrs below
attrs twin raises: TypeError Could not disambiguate between <class '__main__.A1'>, <class '__main__.A2'>: no unique non-default attributes
D1(1) round-trips to D2(x=1) in both union orders, with no error. The same classes written with attrs and Factory(list) raise when the union hook is built.
Expected behaviour
A field with default_factory should count as having a default, the same as an attrs Factory and a dataclass plain default. The dataclass union should then raise the same "Could not disambiguate" TypeError as the attrs version, instead of picking tags as the key and falling back to D2.
Versions
- Python 3.12.3
- cattrs 26.2.1 (26.1.0 gives the same output)
- attrs 26.1.0
- OS: Linux
Cause
In create_default_dis_func (src/cattrs/disambiguators.py, main at 73fa273), line 154 takes cl_fields = fields_dict(get_origin(cl) or cl) and line 157 treats a field as required when cl_fields[orig_name].default in (NOTHING, MISSING). For a dataclass, fields_dict (src/cattrs/_compat.py lines 131-135) returns the raw dataclasses.Field, whose .default is MISSING when only default_factory is set. adapted_fields (_compat.py lines 155-159) already turns default_factory into Factory, but the default check doesn't use it, so tags becomes the unique required field of D1, and a payload without it goes to D2.
Proposed fix
Check defaults on the adapted_fields view, which already maps default_factory to Factory, or also treat default_factory is not MISSING as a default at line 157. A dataclass default_factory case in tests/test_disambiguators.py would cover it; the existing dataclass tests there only use required fields.
I'm happy to open a PR with this fix and a test.
- Lenguaje dominante
- Python
- Estrellas
- 1.1k
- Forks
- 171
- Merge medio
- 14 h 46 min
- PR fusionados (30 d)
- 5
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de python-attrs/cattrs
-
decimal.Decimal not handled in preconf converters (json, pyyaml)Posiblemente ocupada @Binilkks la tomó hace 112 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
python-attrs/cattrs#761 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
python-attrs/cattrs#513 · 4 comentarios ·
Los mantenedores suelen responder en 1 día
-
Structure hook on a PEP 695 alias is ignored inside a passthrough unionPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 62/100
python-attrs/cattrs#797 ·
Los mantenedores suelen responder en 1 día
-
msgspec converter skips hooks and drops private fields in nested recursionPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 3/5 Medio día Aptitud para principiantes 64/100
python-attrs/cattrs#795 ·
Los mantenedores suelen responder en 1 día
-
Getting the next structure hookAbierto
Dificultad 4/5 3-5 días Aptitud para principiantes 25/100
python-attrs/cattrs#794 ·
Los mantenedores suelen responder en 1 día
Todos los issues de python-attrs/cattrs
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
omegaup/omegaup#10353 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
bot-triaged module: LrScheduler module: optimizer triaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 1 día
-
documentation glm5 llm-model
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
vllm-project/vllm-ascend#19062 ·
Los mantenedores suelen responder en 1 día
-
Author Page: shuai-zhao-llmAbiertoauthor-page correction
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
acl-org/acl-anthology#10496 · 1 comentario ·
Los mantenedores suelen responder en 1 día