Skip to content

Commit 53ec7c8

Browse files
gh-142214: Fix two regressions in dataclasses (#142223)
1 parent 128d316 commit 53ec7c8

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

Lib/dataclasses.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,12 @@ def __annotate__(format, /):
550550

551551
new_annotations = {}
552552
for k in annotation_fields:
553-
new_annotations[k] = cls_annotations[k]
553+
# gh-142214: The annotation may be missing in unusual dynamic cases.
554+
# If so, just skip it.
555+
try:
556+
new_annotations[k] = cls_annotations[k]
557+
except KeyError:
558+
pass
554559

555560
if return_type is not MISSING:
556561
if format == Format.STRING:
@@ -1399,9 +1404,10 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields):
13991404
f.type = ann
14001405

14011406
# Fix the class reference in the __annotate__ method
1402-
init_annotate = newcls.__init__.__annotate__
1403-
if getattr(init_annotate, "__generated_by_dataclasses__", False):
1404-
_update_func_cell_for__class__(init_annotate, cls, newcls)
1407+
init = newcls.__init__
1408+
if init_annotate := getattr(init, "__annotate__", None):
1409+
if getattr(init_annotate, "__generated_by_dataclasses__", False):
1410+
_update_func_cell_for__class__(init_annotate, cls, newcls)
14051411

14061412
return newcls
14071413

Lib/test/test_dataclasses/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,20 @@ class C:
927927

928928
validate_class(C)
929929

930+
def test_incomplete_annotations(self):
931+
# gh-142214
932+
@dataclass
933+
class C:
934+
"doc" # needed because otherwise we fetch the annotations at the wrong time
935+
x: int
936+
937+
C.__annotate__ = lambda _: {}
938+
939+
self.assertEqual(
940+
annotationlib.get_annotations(C.__init__),
941+
{"return": None}
942+
)
943+
930944
def test_missing_default(self):
931945
# Test that MISSING works the same as a default not being
932946
# specified.
@@ -2578,6 +2592,20 @@ def __init__(self, x: int) -> None:
25782592

25792593
self.assertFalse(hasattr(E.__init__.__annotate__, "__generated_by_dataclasses__"))
25802594

2595+
def test_slots_true_init_false(self):
2596+
# Test that slots=True and init=False work together and
2597+
# that __annotate__ is not added to __init__.
2598+
2599+
@dataclass(slots=True, init=False)
2600+
class F:
2601+
x: int
2602+
2603+
f = F()
2604+
f.x = 10
2605+
self.assertEqual(f.x, 10)
2606+
2607+
self.assertFalse(hasattr(F.__init__, "__annotate__"))
2608+
25812609
def test_init_false_forwardref(self):
25822610
# Test forward references in fields not required for __init__ annotations.
25832611

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Fix two regressions in :mod:`dataclasses` in Python 3.14.1 related to
2+
annotations.
3+
4+
* An exception is no longer raised if ``slots=True`` is used and the
5+
``__init__`` method does not have an ``__annotate__`` attribute
6+
(likely because ``init=False`` was used).
7+
8+
* An exception is no longer raised if annotations are requested on the
9+
``__init__`` method and one of the fields is not present in the class
10+
annotations. This can occur in certain dynamic scenarios.
11+
12+
Patch by Jelle Zijlstra.

0 commit comments

Comments
 (0)