summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/cpython/funcobject.h1
-rw-r--r--Include/internal/pycore_global_objects_fini_generated.h1
-rw-r--r--Include/internal/pycore_global_strings.h1
-rw-r--r--Include/internal/pycore_runtime_init_generated.h1
-rw-r--r--Include/internal/pycore_unicodeobject_generated.h3
-rw-r--r--Lib/test/test_sys.py2
-rw-r--r--Lib/test/test_type_annotations.py44
-rw-r--r--Lib/test/test_typing.py2
-rw-r--r--Lib/typing.py1
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-05-20-10-10-51.gh-issue-119180.35xqpu.rst2
-rw-r--r--Objects/funcobject.c64
-rw-r--r--Objects/moduleobject.c103
-rw-r--r--Objects/typeobject.c117
13 files changed, 324 insertions, 18 deletions
diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h
index 5433ba4..598cd33 100644
--- a/Include/cpython/funcobject.h
+++ b/Include/cpython/funcobject.h
@@ -41,6 +41,7 @@ typedef struct {
PyObject *func_weakreflist; /* List of weak references */
PyObject *func_module; /* The __module__ attribute, can be anything */
PyObject *func_annotations; /* Annotations, a dict or NULL */
+ PyObject *func_annotate; /* Callable to fill the annotations dictionary */
PyObject *func_typeparams; /* Tuple of active type variables or NULL */
vectorcallfunc vectorcall;
/* Version number for use by specializer.
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h
index ca7355b..33133aa 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -590,6 +590,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__all__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__and__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__anext__));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__annotate__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__annotations__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__args__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__asyncio_running_event_loop__));
diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h
index fbb2528..f5ea7b9 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -79,6 +79,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__all__)
STRUCT_FOR_ID(__and__)
STRUCT_FOR_ID(__anext__)
+ STRUCT_FOR_ID(__annotate__)
STRUCT_FOR_ID(__annotations__)
STRUCT_FOR_ID(__args__)
STRUCT_FOR_ID(__asyncio_running_event_loop__)
diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h
index 508da40..c73408d 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -588,6 +588,7 @@ extern "C" {
INIT_ID(__all__), \
INIT_ID(__and__), \
INIT_ID(__anext__), \
+ INIT_ID(__annotate__), \
INIT_ID(__annotations__), \
INIT_ID(__args__), \
INIT_ID(__asyncio_running_event_loop__), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h
index cc2fc15..d84c45a 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -78,6 +78,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(__anext__);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(__annotate__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(__annotations__);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index ee3bd00..8fe1d77 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1564,7 +1564,7 @@ class SizeofTest(unittest.TestCase):
check(x, size('3Pi2cP7P2ic??2P'))
# function
def func(): pass
- check(func, size('15Pi'))
+ check(func, size('16Pi'))
class c():
@staticmethod
def foo():
diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py
index 3dbb35a..5e3c334 100644
--- a/Lib/test/test_type_annotations.py
+++ b/Lib/test/test_type_annotations.py
@@ -1,4 +1,5 @@
import textwrap
+import types
import unittest
from test.support import run_code
@@ -212,3 +213,46 @@ class TestSetupAnnotations(unittest.TestCase):
case 0:
x: int = 1
""")
+
+
+class AnnotateTests(unittest.TestCase):
+ """See PEP 649."""
+ def test_manual_annotate(self):
+ def f():
+ pass
+ mod = types.ModuleType("mod")
+ class X:
+ pass
+
+ for obj in (f, mod, X):
+ with self.subTest(obj=obj):
+ self.check_annotations(obj)
+
+ def check_annotations(self, f):
+ self.assertEqual(f.__annotations__, {})
+ self.assertIs(f.__annotate__, None)
+
+ with self.assertRaisesRegex(TypeError, "__annotate__ must be callable or None"):
+ f.__annotate__ = 42
+ f.__annotate__ = lambda: 42
+ with self.assertRaisesRegex(TypeError, r"takes 0 positional arguments but 1 was given"):
+ print(f.__annotations__)
+
+ f.__annotate__ = lambda x: 42
+ with self.assertRaisesRegex(TypeError, r"__annotate__ returned non-dict of type 'int'"):
+ print(f.__annotations__)
+
+ f.__annotate__ = lambda x: {"x": x}
+ self.assertEqual(f.__annotations__, {"x": 1})
+
+ # Setting annotate to None does not invalidate the cached __annotations__
+ f.__annotate__ = None
+ self.assertEqual(f.__annotations__, {"x": 1})
+
+ # But setting it to a new callable does
+ f.__annotate__ = lambda x: {"y": x}
+ self.assertEqual(f.__annotations__, {"y": 1})
+
+ # Setting f.__annotations__ also clears __annotate__
+ f.__annotations__ = {"z": 43}
+ self.assertIs(f.__annotate__, None)
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 64c4c49..dac55ce 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -3723,7 +3723,7 @@ class ProtocolTests(BaseTestCase):
acceptable_extra_attrs = {
'_is_protocol', '_is_runtime_protocol', '__parameters__',
- '__init__', '__annotations__', '__subclasshook__',
+ '__init__', '__annotations__', '__subclasshook__', '__annotate__',
}
self.assertLessEqual(vars(NonP).keys(), vars(C).keys() | acceptable_extra_attrs)
self.assertLessEqual(
diff --git a/Lib/typing.py b/Lib/typing.py
index 4345745..be49aa6 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -1889,6 +1889,7 @@ _SPECIAL_NAMES = frozenset({
'__init__', '__module__', '__new__', '__slots__',
'__subclasshook__', '__weakref__', '__class_getitem__',
'__match_args__', '__static_attributes__', '__firstlineno__',
+ '__annotate__',
})
# These special attributes will be not collected as protocol members.
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-20-10-10-51.gh-issue-119180.35xqpu.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-20-10-10-51.gh-issue-119180.35xqpu.rst
new file mode 100644
index 0000000..5a88ce0
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-20-10-10-51.gh-issue-119180.35xqpu.rst
@@ -0,0 +1,2 @@
+Add an ``__annotate__`` attribute to functions, classes, and modules as part
+of :pep:`649`. Patch by Jelle Zijlstra.
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 8a30213..4e78252 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -3,6 +3,7 @@
#include "Python.h"
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
+#include "pycore_long.h" // _PyLong_GetOne()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
@@ -124,6 +125,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr)
op->func_weakreflist = NULL;
op->func_module = module;
op->func_annotations = NULL;
+ op->func_annotate = NULL;
op->func_typeparams = NULL;
op->vectorcall = _PyFunction_Vectorcall;
op->func_version = 0;
@@ -202,6 +204,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
op->func_weakreflist = NULL;
op->func_module = module;
op->func_annotations = NULL;
+ op->func_annotate = NULL;
op->func_typeparams = NULL;
op->vectorcall = _PyFunction_Vectorcall;
op->func_version = 0;
@@ -512,7 +515,22 @@ static PyObject *
func_get_annotation_dict(PyFunctionObject *op)
{
if (op->func_annotations == NULL) {
- return NULL;
+ if (op->func_annotate == NULL || !PyCallable_Check(op->func_annotate)) {
+ Py_RETURN_NONE;
+ }
+ PyObject *one = _PyLong_GetOne();
+ PyObject *ann_dict = _PyObject_CallOneArg(op->func_annotate, one);
+ if (ann_dict == NULL) {
+ return NULL;
+ }
+ if (!PyDict_Check(ann_dict)) {
+ PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'",
+ Py_TYPE(ann_dict)->tp_name);
+ Py_DECREF(ann_dict);
+ return NULL;
+ }
+ Py_XSETREF(op->func_annotations, ann_dict);
+ return ann_dict;
}
if (PyTuple_CheckExact(op->func_annotations)) {
PyObject *ann_tuple = op->func_annotations;
@@ -565,7 +583,9 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
"non-dict annotations");
return -1;
}
- Py_XSETREF(((PyFunctionObject *)op)->func_annotations, annotations);
+ PyFunctionObject *func = (PyFunctionObject *)op;
+ Py_XSETREF(func->func_annotations, annotations);
+ Py_CLEAR(func->func_annotate);
return 0;
}
@@ -764,9 +784,43 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor
}
static PyObject *
+func_get_annotate(PyFunctionObject *op, void *Py_UNUSED(ignored))
+{
+ if (op->func_annotate == NULL) {
+ Py_RETURN_NONE;
+ }
+ return Py_NewRef(op->func_annotate);
+}
+
+static int
+func_set_annotate(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "__annotate__ cannot be deleted");
+ return -1;
+ }
+ if (Py_IsNone(value)) {
+ Py_XSETREF(op->func_annotate, value);
+ return 0;
+ }
+ else if (PyCallable_Check(value)) {
+ Py_XSETREF(op->func_annotate, Py_XNewRef(value));
+ Py_CLEAR(op->func_annotations);
+ return 0;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "__annotate__ must be callable or None");
+ return -1;
+ }
+}
+
+static PyObject *
func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored))
{
- if (op->func_annotations == NULL) {
+ if (op->func_annotations == NULL &&
+ (op->func_annotate == NULL || !PyCallable_Check(op->func_annotate))) {
op->func_annotations = PyDict_New();
if (op->func_annotations == NULL)
return NULL;
@@ -789,6 +843,7 @@ func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(igno
return -1;
}
Py_XSETREF(op->func_annotations, Py_XNewRef(value));
+ Py_CLEAR(op->func_annotate);
return 0;
}
@@ -836,6 +891,7 @@ static PyGetSetDef func_getsetlist[] = {
(setter)func_set_kwdefaults},
{"__annotations__", (getter)func_get_annotations,
(setter)func_set_annotations},
+ {"__annotate__", (getter)func_get_annotate, (setter)func_set_annotate},
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
{"__name__", (getter)func_get_name, (setter)func_set_name},
{"__qualname__", (getter)func_get_qualname, (setter)func_set_qualname},
@@ -972,6 +1028,7 @@ func_clear(PyFunctionObject *op)
Py_CLEAR(op->func_dict);
Py_CLEAR(op->func_closure);
Py_CLEAR(op->func_annotations);
+ Py_CLEAR(op->func_annotate);
Py_CLEAR(op->func_typeparams);
// Don't Py_CLEAR(op->func_code), since code is always required
// to be non-NULL. Similarly, name and qualname shouldn't be NULL.
@@ -1028,6 +1085,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
Py_VISIT(f->func_dict);
Py_VISIT(f->func_closure);
Py_VISIT(f->func_annotations);
+ Py_VISIT(f->func_annotate);
Py_VISIT(f->func_typeparams);
Py_VISIT(f->func_qualname);
return 0;
diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c
index 46995b9..73ad971 100644
--- a/Objects/moduleobject.c
+++ b/Objects/moduleobject.c
@@ -5,6 +5,7 @@
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_fileutils.h" // _Py_wgetcwd
#include "pycore_interp.h" // PyInterpreterState.importlib
+#include "pycore_long.h" // _PyLong_GetOne()
#include "pycore_modsupport.h" // _PyModule_CreateInitialized()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_AllocNoTrack
@@ -1133,7 +1134,7 @@ static PyMethodDef module_methods[] = {
};
static PyObject *
-module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored))
+module_get_dict(PyModuleObject *m)
{
PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__));
if (dict == NULL) {
@@ -1144,10 +1145,97 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored))
Py_DECREF(dict);
return NULL;
}
+ return dict;
+}
+
+static PyObject *
+module_get_annotate(PyModuleObject *m, void *Py_UNUSED(ignored))
+{
+ PyObject *dict = module_get_dict(m);
+ if (dict == NULL) {
+ return NULL;
+ }
+
+ PyObject *annotate;
+ if (PyDict_GetItemRef(dict, &_Py_ID(__annotate__), &annotate) == 0) {
+ annotate = Py_None;
+ if (PyDict_SetItem(dict, &_Py_ID(__annotate__), annotate) == -1) {
+ Py_CLEAR(annotate);
+ }
+ }
+ Py_DECREF(dict);
+ return annotate;
+}
+
+static int
+module_set_annotate(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignored))
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete __annotate__ attribute");
+ return -1;
+ }
+ PyObject *dict = module_get_dict(m);
+ if (dict == NULL) {
+ return -1;
+ }
+
+ if (!Py_IsNone(value) && !PyCallable_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "__annotate__ must be callable or None");
+ Py_DECREF(dict);
+ return -1;
+ }
+
+ if (PyDict_SetItem(dict, &_Py_ID(__annotate__), value) == -1) {
+ Py_DECREF(dict);
+ return -1;
+ }
+ if (!Py_IsNone(value)) {
+ if (PyDict_Pop(dict, &_Py_ID(__annotations__), NULL) == -1) {
+ Py_DECREF(dict);
+ return -1;
+ }
+ }
+ Py_DECREF(dict);
+ return 0;
+}
+
+static PyObject *
+module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored))
+{
+ PyObject *dict = module_get_dict(m);
+ if (dict == NULL) {
+ return NULL;
+ }
PyObject *annotations;
if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) == 0) {
- annotations = PyDict_New();
+ PyObject *annotate;
+ int annotate_result = PyDict_GetItemRef(dict, &_Py_ID(__annotate__), &annotate);
+ if (annotate_result < 0) {
+ Py_DECREF(dict);
+ return NULL;
+ }
+ if (annotate_result == 1 && PyCallable_Check(annotate)) {
+ PyObject *one = _PyLong_GetOne();
+ annotations = _PyObject_CallOneArg(annotate, one);
+ if (annotations == NULL) {
+ Py_DECREF(annotate);
+ Py_DECREF(dict);
+ return NULL;
+ }
+ if (!PyDict_Check(annotations)) {
+ PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'",
+ Py_TYPE(annotations)->tp_name);
+ Py_DECREF(annotate);
+ Py_DECREF(annotations);
+ Py_DECREF(dict);
+ return NULL;
+ }
+ }
+ else {
+ annotations = PyDict_New();
+ }
+ Py_XDECREF(annotate);
if (annotations) {
int result = PyDict_SetItem(
dict, &_Py_ID(__annotations__), annotations);
@@ -1164,14 +1252,10 @@ static int
module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignored))
{
int ret = -1;
- PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__));
+ PyObject *dict = module_get_dict(m);
if (dict == NULL) {
return -1;
}
- if (!PyDict_Check(dict)) {
- PyErr_Format(PyExc_TypeError, "<module>.__dict__ is not a dictionary");
- goto exit;
- }
if (value != NULL) {
/* set */
@@ -1188,8 +1272,10 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor
ret = 0;
}
}
+ if (ret == 0 && PyDict_Pop(dict, &_Py_ID(__annotate__), NULL) < 0) {
+ ret = -1;
+ }
-exit:
Py_DECREF(dict);
return ret;
}
@@ -1197,6 +1283,7 @@ exit:
static PyGetSetDef module_getsets[] = {
{"__annotations__", (getter)module_get_annotations, (setter)module_set_annotations},
+ {"__annotate__", (getter)module_get_annotate, (setter)module_set_annotate},
{NULL}
};
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index b7c3fcf..9f000d8 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -7,7 +7,7 @@
#include "pycore_dict.h" // _PyDict_KeysSize()
#include "pycore_frame.h" // _PyInterpreterFrame
#include "pycore_lock.h" // _PySeqLock_*
-#include "pycore_long.h" // _PyLong_IsNegative()
+#include "pycore_long.h" // _PyLong_IsNegative(), _PyLong_GetOne()
#include "pycore_memoryobject.h" // _PyMemoryView_FromBufferProc()
#include "pycore_modsupport.h" // _PyArg_NoKwnames()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
@@ -1675,6 +1675,76 @@ type_set_doc(PyTypeObject *type, PyObject *value, void *context)
}
static PyObject *
+type_get_annotate(PyTypeObject *type, void *Py_UNUSED(ignored))
+{
+ if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
+ PyErr_Format(PyExc_AttributeError, "type object '%s' has no attribute '__annotate__'", type->tp_name);
+ return NULL;
+ }
+
+ PyObject *annotate;
+ PyObject *dict = PyType_GetDict(type);
+ if (PyDict_GetItemRef(dict, &_Py_ID(__annotate__), &annotate) < 0) {
+ Py_DECREF(dict);
+ return NULL;
+ }
+ if (annotate) {
+ descrgetfunc get = Py_TYPE(annotate)->tp_descr_get;
+ if (get) {
+ Py_SETREF(annotate, get(annotate, NULL, (PyObject *)type));
+ }
+ }
+ else {
+ annotate = Py_None;
+ int result = PyDict_SetItem(dict, &_Py_ID(__annotate__), annotate);
+ if (result < 0) {
+ Py_DECREF(dict);
+ return NULL;
+ }
+ }
+ Py_DECREF(dict);
+ return annotate;
+}
+
+static int
+type_set_annotate(PyTypeObject *type, PyObject *value, void *Py_UNUSED(ignored))
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete __annotate__ attribute");
+ return -1;
+ }
+ if (_PyType_HasFeature(type, Py_TPFLAGS_IMMUTABLETYPE)) {
+ PyErr_Format(PyExc_TypeError,
+ "cannot set '__annotate__' attribute of immutable type '%s'",
+ type->tp_name);
+ return -1;
+ }
+
+ if (!Py_IsNone(value) && !PyCallable_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "__annotate__ must be callable or None");
+ return -1;
+ }
+
+ PyObject *dict = PyType_GetDict(type);
+ assert(PyDict_Check(dict));
+ int result = PyDict_SetItem(dict, &_Py_ID(__annotate__), value);
+ if (result < 0) {
+ Py_DECREF(dict);
+ return -1;
+ }
+ if (!Py_IsNone(value)) {
+ if (PyDict_Pop(dict, &_Py_ID(__annotations__), NULL) == -1) {
+ Py_DECREF(dict);
+ PyType_Modified(type);
+ return -1;
+ }
+ }
+ Py_DECREF(dict);
+ PyType_Modified(type);
+ return 0;
+}
+
+static PyObject *
type_get_annotations(PyTypeObject *type, void *context)
{
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
@@ -1683,8 +1753,9 @@ type_get_annotations(PyTypeObject *type, void *context)
}
PyObject *annotations;
- PyObject *dict = lookup_tp_dict(type);
+ PyObject *dict = PyType_GetDict(type);
if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) < 0) {
+ Py_DECREF(dict);
return NULL;
}
if (annotations) {
@@ -1694,7 +1765,32 @@ type_get_annotations(PyTypeObject *type, void *context)
}
}
else {
- annotations = PyDict_New();
+ PyObject *annotate = type_get_annotate(type, NULL);
+ if (annotate == NULL) {
+ Py_DECREF(dict);
+ return NULL;
+ }
+ if (PyCallable_Check(annotate)) {
+ PyObject *one = _PyLong_GetOne();
+ annotations = _PyObject_CallOneArg(annotate, one);
+ if (annotations == NULL) {
+ Py_DECREF(dict);
+ Py_DECREF(annotate);
+ return NULL;
+ }
+ if (!PyDict_Check(annotations)) {
+ PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'",
+ Py_TYPE(annotations)->tp_name);
+ Py_DECREF(annotations);
+ Py_DECREF(annotate);
+ Py_DECREF(dict);
+ return NULL;
+ }
+ }
+ else {
+ annotations = PyDict_New();
+ }
+ Py_DECREF(annotate);
if (annotations) {
int result = PyDict_SetItem(
dict, &_Py_ID(__annotations__), annotations);
@@ -1705,6 +1801,7 @@ type_get_annotations(PyTypeObject *type, void *context)
}
}
}
+ Py_DECREF(dict);
return annotations;
}
@@ -1719,7 +1816,7 @@ type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
}
int result;
- PyObject *dict = lookup_tp_dict(type);
+ PyObject *dict = PyType_GetDict(type);
if (value != NULL) {
/* set */
result = PyDict_SetItem(dict, &_Py_ID(__annotations__), value);
@@ -1728,14 +1825,23 @@ type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
result = PyDict_Pop(dict, &_Py_ID(__annotations__), NULL);
if (result == 0) {
PyErr_SetString(PyExc_AttributeError, "__annotations__");
+ Py_DECREF(dict);
return -1;
}
}
if (result < 0) {
+ Py_DECREF(dict);
return -1;
}
-
+ else if (result == 0) {
+ if (PyDict_Pop(dict, &_Py_ID(__annotate__), NULL) < 0) {
+ PyType_Modified(type);
+ Py_DECREF(dict);
+ return -1;
+ }
+ }
PyType_Modified(type);
+ Py_DECREF(dict);
return 0;
}
@@ -1811,6 +1917,7 @@ static PyGetSetDef type_getsets[] = {
{"__doc__", (getter)type_get_doc, (setter)type_set_doc, NULL},
{"__text_signature__", (getter)type_get_text_signature, NULL, NULL},
{"__annotations__", (getter)type_get_annotations, (setter)type_set_annotations, NULL},
+ {"__annotate__", (getter)type_get_annotate, (setter)type_set_annotate, NULL},
{"__type_params__", (getter)type_get_type_params, (setter)type_set_type_params, NULL},
{0}
};