mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-142214: Fix two regressions in dataclasses (#142223)
This commit is contained in:
parent
128d31637e
commit
53ec7c8fc0
3 changed files with 50 additions and 4 deletions
|
|
@ -550,7 +550,12 @@ def __annotate__(format, /):
|
||||||
|
|
||||||
new_annotations = {}
|
new_annotations = {}
|
||||||
for k in annotation_fields:
|
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 return_type is not MISSING:
|
||||||
if format == Format.STRING:
|
if format == Format.STRING:
|
||||||
|
|
@ -1399,9 +1404,10 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields):
|
||||||
f.type = ann
|
f.type = ann
|
||||||
|
|
||||||
# Fix the class reference in the __annotate__ method
|
# Fix the class reference in the __annotate__ method
|
||||||
init_annotate = newcls.__init__.__annotate__
|
init = newcls.__init__
|
||||||
if getattr(init_annotate, "__generated_by_dataclasses__", False):
|
if init_annotate := getattr(init, "__annotate__", None):
|
||||||
_update_func_cell_for__class__(init_annotate, cls, newcls)
|
if getattr(init_annotate, "__generated_by_dataclasses__", False):
|
||||||
|
_update_func_cell_for__class__(init_annotate, cls, newcls)
|
||||||
|
|
||||||
return newcls
|
return newcls
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -927,6 +927,20 @@ class C:
|
||||||
|
|
||||||
validate_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):
|
def test_missing_default(self):
|
||||||
# Test that MISSING works the same as a default not being
|
# Test that MISSING works the same as a default not being
|
||||||
# specified.
|
# specified.
|
||||||
|
|
@ -2578,6 +2592,20 @@ def __init__(self, x: int) -> None:
|
||||||
|
|
||||||
self.assertFalse(hasattr(E.__init__.__annotate__, "__generated_by_dataclasses__"))
|
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):
|
def test_init_false_forwardref(self):
|
||||||
# Test forward references in fields not required for __init__ annotations.
|
# Test forward references in fields not required for __init__ annotations.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue