Dataclass default_factory field treated as required in union disambiguation
Maintainer antworten meist innerhalb von 1 Tag
@sammist2 arbeitet bereits daran.
Seit 17.10.2026.
Bewertung
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Anfängerfreundlichkeit
- 72/100
Rechercherichtung
Führe zuerst das Reproduktionsskript aus dem Issue aus, um zu bestätigen, dass die Dataclass-Union für eine Payload ohne tags D2 zurückgibt. Die Default-Prüfung befindet sich in create_default_dis_func in src/cattrs/disambiguators.py etwa in den Zeilen 154-157, und fields_dict sowie adapted_fields in src/cattrs/_compat.py zeigen, warum default_factory dort unsichtbar ist. Fertig ist es, wenn sich der Dataclass-Fall wie das Verhalten bei attrs verhält und ein Test für default_factory in tests/test_disambiguators.py hinzugefügt wurde.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
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.
- Vorherrschende Sprache
- Python
- Sterne
- 1.1k
- Forks
- 189
- Ø Merge
- 20 Std. 11 Min.
- Gemergte PRs (30 T.)
- 10
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus python-attrs/cattrs
-
decimal.Decimal not handled in preconf converters (json, pyyaml)Evtl. vergeben @Binilkks hat das vor 132 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
python-attrs/cattrs#761 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 64/100
python-attrs/cattrs#513 · 4 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
Structure hook on a PEP 695 alias is ignored inside a passthrough unionEvtl. vergeben @vyrnsynx hat das vor 12 Tagen übernommen. Offen
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 62/100
python-attrs/cattrs#797 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 25/100
python-attrs/cattrs#794 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Make a Converter without the default hook factoriesEvtl. wieder frei Ein Pull Request dazu wurde ohne Merge geschlossen. Offen
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 25/100
python-attrs/cattrs#793 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in python-attrs/cattrs
Ähnliche Issues
-
follow-up needs-triage S3: minor
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
manaflow-ai/cmux#36856 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
documentation good first issue help wanted
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 90/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 73/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
-
Shell framework build fails at "copy the shader libraries": the iOS shader libraries are now one file b-<chain>.imgEvtl. vergeben @xiaofeng-han hat das heute übernommen. Offenbuild ios
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
Maintainer antworten meist innerhalb von 1 Tag