Dataclass default_factory field treated as required in union disambiguation
Les mainteneurs répondent en général sous 1 jour
@sammist2 y travaille déjà.
Depuis le 17/10/2026.
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 72/100
Piste de recherche
Commencez par exécuter le script de reproduction de l'issue pour confirmer que l'union de dataclass renvoie D2 pour une charge utile sans tags. La vérification de la valeur par défaut se trouve dans create_default_dis_func dans src/cattrs/disambiguators.py, autour des lignes 154-157, et fields_dict ainsi que adapted_fields dans src/cattrs/_compat.py montrent pourquoi default_factory y est invisible. C'est terminé lorsque le cas des dataclass correspond au comportement d'attrs, avec un test de default_factory ajouté dans tests/test_disambiguators.py.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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.
- Langage dominant
- Python
- Étoiles
- 1.1k
- Forks
- 189
- Merge moyen
- 22 h 19 min
- PR mergées (30 j)
- 8
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de python-attrs/cattrs
-
decimal.Decimal not handled in preconf converters (json, pyyaml)Peut-être pris @Binilkks l’a pris il y a 129 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
python-attrs/cattrs#761 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 64/100
python-attrs/cattrs#513 · 4 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
msgspec converter cannot serialize typed dequesPeut-être pris @HocVoNgThai l’a pris il y a 1 jour. Ouverte
Difficulté 3/5 Une demi-journée Accessibilité débutants 25/100
python-attrs/cattrs#818 ·
Les mainteneurs répondent en général sous 1 jour
-
Structure hook on a PEP 695 alias is ignored inside a passthrough unionPeut-être pris @vyrnsynx l’a pris il y a 9 jours. Ouverte
Difficulté 3/5 1-2 jours Accessibilité débutants 62/100
python-attrs/cattrs#797 ·
Les mainteneurs répondent en général sous 1 jour
-
Getting the next structure hookOuverte
Difficulté 4/5 3-5 jours Accessibilité débutants 25/100
python-attrs/cattrs#794 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de python-attrs/cattrs
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
earthtojake/text-to-cad#758 ·
Les mainteneurs répondent en général sous 1 jour
-
domain:upgrades enhancement needs-triage
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
gke-labs/kube-agents#4634 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
agardnerIT/opamp-server-py#116 ·
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 1 jour
-
enhancement good first issue
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
elviskahoro/sdk-python-attio#144 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour