[3.14] gh-133778: Fix setting __annotations__
under PEP 563 (GH-133794) (#134655)
gh-133778: Fix setting `__annotations__` under PEP 563 (GH-133794) (cherry picked from commit 4443110c3409ecba9f0fd49495d039030cb7ed58) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
93aee568c0
commit
d82d445b18
@ -498,6 +498,28 @@ class DeferredEvaluationTests(unittest.TestCase):
|
|||||||
self.assertEqual(f.__annotate__(annotationlib.Format.VALUE), annos)
|
self.assertEqual(f.__annotate__(annotationlib.Format.VALUE), annos)
|
||||||
self.assertEqual(f.__annotations__, annos)
|
self.assertEqual(f.__annotations__, annos)
|
||||||
|
|
||||||
|
def test_set_annotations(self):
|
||||||
|
function_code = textwrap.dedent("""
|
||||||
|
def f(x: int):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
class_code = textwrap.dedent("""
|
||||||
|
class f:
|
||||||
|
x: int
|
||||||
|
""")
|
||||||
|
for future in (False, True):
|
||||||
|
for label, code in (("function", function_code), ("class", class_code)):
|
||||||
|
with self.subTest(future=future, label=label):
|
||||||
|
if future:
|
||||||
|
code = "from __future__ import annotations\n" + code
|
||||||
|
ns = run_code(code)
|
||||||
|
f = ns["f"]
|
||||||
|
anno = "int" if future else int
|
||||||
|
self.assertEqual(f.__annotations__, {"x": anno})
|
||||||
|
|
||||||
|
f.__annotations__ = {"x": str}
|
||||||
|
self.assertEqual(f.__annotations__, {"x": str})
|
||||||
|
|
||||||
def test_name_clash_with_format(self):
|
def test_name_clash_with_format(self):
|
||||||
# this test would fail if __annotate__'s parameter was called "format"
|
# this test would fail if __annotate__'s parameter was called "format"
|
||||||
# during symbol table construction
|
# during symbol table construction
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
Fix bug where assigning to the :attr:`~type.__annotations__` attributes of
|
||||||
|
classes defined under ``from __future__ import annotations`` had no effect.
|
@ -2065,19 +2065,46 @@ type_set_annotations(PyObject *tp, PyObject *value, void *Py_UNUSED(closure))
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result;
|
|
||||||
PyObject *dict = PyType_GetDict(type);
|
PyObject *dict = PyType_GetDict(type);
|
||||||
if (value != NULL) {
|
int result = PyDict_ContainsString(dict, "__annotations__");
|
||||||
/* set */
|
if (result < 0) {
|
||||||
result = PyDict_SetItem(dict, &_Py_ID(__annotations_cache__), value);
|
Py_DECREF(dict);
|
||||||
} else {
|
return -1;
|
||||||
/* delete */
|
}
|
||||||
result = PyDict_Pop(dict, &_Py_ID(__annotations_cache__), NULL);
|
if (result) {
|
||||||
if (result == 0) {
|
// If __annotations__ is currently in the dict, we update it,
|
||||||
PyErr_SetString(PyExc_AttributeError, "__annotations__");
|
if (value != NULL) {
|
||||||
|
result = PyDict_SetItem(dict, &_Py_ID(__annotations__), value);
|
||||||
|
} else {
|
||||||
|
result = PyDict_Pop(dict, &_Py_ID(__annotations__), NULL);
|
||||||
|
if (result == 0) {
|
||||||
|
// Somebody else just deleted it?
|
||||||
|
PyErr_SetString(PyExc_AttributeError, "__annotations__");
|
||||||
|
Py_DECREF(dict);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result < 0) {
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
// Also clear __annotations_cache__ just in case.
|
||||||
|
result = PyDict_Pop(dict, &_Py_ID(__annotations_cache__), NULL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Else we update only __annotations_cache__.
|
||||||
|
if (value != NULL) {
|
||||||
|
/* set */
|
||||||
|
result = PyDict_SetItem(dict, &_Py_ID(__annotations_cache__), value);
|
||||||
|
} else {
|
||||||
|
/* delete */
|
||||||
|
result = PyDict_Pop(dict, &_Py_ID(__annotations_cache__), NULL);
|
||||||
|
if (result == 0) {
|
||||||
|
PyErr_SetString(PyExc_AttributeError, "__annotations__");
|
||||||
|
Py_DECREF(dict);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user