https://github.com/python/cpython/commit/53ec7c8fc07eb6958869638a0cad70c52ad6fcf5
commit: 53ec7c8fc07eb6958869638a0cad70c52ad6fcf5
branch: main
author: Jelle Zijlstra <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2025-12-04T20:04:42-08:00
summary:
gh-142214: Fix two regressions in dataclasses (#142223)
files:
A Misc/NEWS.d/next/Library/2025-12-03-06-12-39.gh-issue-142214.appYNZ.rst
M Lib/dataclasses.py
M Lib/test/test_dataclasses/__init__.py
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 3ccb72469286eb..730ced7299865e 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -550,7 +550,12 @@ def __annotate__(format, /):
new_annotations = {}
for k in annotation_fields:
- new_annotations[k] = cls_annotations[k]
+ # gh-142214: The annotation may be missing in unusual
dynamic cases.
+ # If so, just skip it.
+ try:
+ new_annotations[k] = cls_annotations[k]
+ except KeyError:
+ pass
if return_type is not MISSING:
if format == Format.STRING:
@@ -1399,9 +1404,10 @@ def _add_slots(cls, is_frozen, weakref_slot,
defined_fields):
f.type = ann
# Fix the class reference in the __annotate__ method
- init_annotate = newcls.__init__.__annotate__
- if getattr(init_annotate, "__generated_by_dataclasses__", False):
- _update_func_cell_for__class__(init_annotate, cls, newcls)
+ init = newcls.__init__
+ if init_annotate := getattr(init, "__annotate__", None):
+ if getattr(init_annotate, "__generated_by_dataclasses__", False):
+ _update_func_cell_for__class__(init_annotate, cls, newcls)
return newcls
diff --git a/Lib/test/test_dataclasses/__init__.py
b/Lib/test/test_dataclasses/__init__.py
index 513dd78c4381b4..3b335429b98500 100644
--- a/Lib/test/test_dataclasses/__init__.py
+++ b/Lib/test/test_dataclasses/__init__.py
@@ -927,6 +927,20 @@ class C:
validate_class(C)
+ def test_incomplete_annotations(self):
+ # gh-142214
+ @dataclass
+ class C:
+ "doc" # needed because otherwise we fetch the annotations at the
wrong time
+ x: int
+
+ C.__annotate__ = lambda _: {}
+
+ self.assertEqual(
+ annotationlib.get_annotations(C.__init__),
+ {"return": None}
+ )
+
def test_missing_default(self):
# Test that MISSING works the same as a default not being
# specified.
@@ -2578,6 +2592,20 @@ def __init__(self, x: int) -> None:
self.assertFalse(hasattr(E.__init__.__annotate__,
"__generated_by_dataclasses__"))
+ def test_slots_true_init_false(self):
+ # Test that slots=True and init=False work together and
+ # that __annotate__ is not added to __init__.
+
+ @dataclass(slots=True, init=False)
+ class F:
+ x: int
+
+ f = F()
+ f.x = 10
+ self.assertEqual(f.x, 10)
+
+ self.assertFalse(hasattr(F.__init__, "__annotate__"))
+
def test_init_false_forwardref(self):
# Test forward references in fields not required for __init__
annotations.
diff --git
a/Misc/NEWS.d/next/Library/2025-12-03-06-12-39.gh-issue-142214.appYNZ.rst
b/Misc/NEWS.d/next/Library/2025-12-03-06-12-39.gh-issue-142214.appYNZ.rst
new file mode 100644
index 00000000000000..b87430ec1a3d65
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-03-06-12-39.gh-issue-142214.appYNZ.rst
@@ -0,0 +1,12 @@
+Fix two regressions in :mod:`dataclasses` in Python 3.14.1 related to
+annotations.
+
+* An exception is no longer raised if ``slots=True`` is used and the
+ ``__init__`` method does not have an ``__annotate__`` attribute
+ (likely because ``init=False`` was used).
+
+* An exception is no longer raised if annotations are requested on the
+ ``__init__`` method and one of the fields is not present in the class
+ annotations. This can occur in certain dynamic scenarios.
+
+Patch by Jelle Zijlstra.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]