mirror of https://github.com/python/cpython
gh-119180: PEP 649: Add __annotate__ attributes (#119209)
This commit is contained in:
parent
73ab83b27f
commit
e9875ecb5d
|
@ -41,6 +41,7 @@ typedef struct {
|
||||||
PyObject *func_weakreflist; /* List of weak references */
|
PyObject *func_weakreflist; /* List of weak references */
|
||||||
PyObject *func_module; /* The __module__ attribute, can be anything */
|
PyObject *func_module; /* The __module__ attribute, can be anything */
|
||||||
PyObject *func_annotations; /* Annotations, a dict or NULL */
|
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 */
|
PyObject *func_typeparams; /* Tuple of active type variables or NULL */
|
||||||
vectorcallfunc vectorcall;
|
vectorcallfunc vectorcall;
|
||||||
/* Version number for use by specializer.
|
/* Version number for use by specializer.
|
||||||
|
|
|
@ -590,6 +590,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__all__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__all__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__and__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__and__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__anext__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__anext__));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__annotate__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__annotations__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__annotations__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__args__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__args__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__asyncio_running_event_loop__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__asyncio_running_event_loop__));
|
||||||
|
|
|
@ -79,6 +79,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(__all__)
|
STRUCT_FOR_ID(__all__)
|
||||||
STRUCT_FOR_ID(__and__)
|
STRUCT_FOR_ID(__and__)
|
||||||
STRUCT_FOR_ID(__anext__)
|
STRUCT_FOR_ID(__anext__)
|
||||||
|
STRUCT_FOR_ID(__annotate__)
|
||||||
STRUCT_FOR_ID(__annotations__)
|
STRUCT_FOR_ID(__annotations__)
|
||||||
STRUCT_FOR_ID(__args__)
|
STRUCT_FOR_ID(__args__)
|
||||||
STRUCT_FOR_ID(__asyncio_running_event_loop__)
|
STRUCT_FOR_ID(__asyncio_running_event_loop__)
|
||||||
|
|
|
@ -588,6 +588,7 @@ extern "C" {
|
||||||
INIT_ID(__all__), \
|
INIT_ID(__all__), \
|
||||||
INIT_ID(__and__), \
|
INIT_ID(__and__), \
|
||||||
INIT_ID(__anext__), \
|
INIT_ID(__anext__), \
|
||||||
|
INIT_ID(__annotate__), \
|
||||||
INIT_ID(__annotations__), \
|
INIT_ID(__annotations__), \
|
||||||
INIT_ID(__args__), \
|
INIT_ID(__args__), \
|
||||||
INIT_ID(__asyncio_running_event_loop__), \
|
INIT_ID(__asyncio_running_event_loop__), \
|
||||||
|
|
|
@ -78,6 +78,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(__anext__);
|
string = &_Py_ID(__anext__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(__annotate__);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(__annotations__);
|
string = &_Py_ID(__annotations__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
|
|
@ -1564,7 +1564,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
check(x, size('3Pi2cP7P2ic??2P'))
|
check(x, size('3Pi2cP7P2ic??2P'))
|
||||||
# function
|
# function
|
||||||
def func(): pass
|
def func(): pass
|
||||||
check(func, size('15Pi'))
|
check(func, size('16Pi'))
|
||||||
class c():
|
class c():
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def foo():
|
def foo():
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
from test.support import run_code
|
from test.support import run_code
|
||||||
|
|
||||||
|
@ -212,3 +213,46 @@ class TestSetupAnnotations(unittest.TestCase):
|
||||||
case 0:
|
case 0:
|
||||||
x: int = 1
|
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)
|
||||||
|
|
|
@ -3723,7 +3723,7 @@ class ProtocolTests(BaseTestCase):
|
||||||
|
|
||||||
acceptable_extra_attrs = {
|
acceptable_extra_attrs = {
|
||||||
'_is_protocol', '_is_runtime_protocol', '__parameters__',
|
'_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(vars(NonP).keys(), vars(C).keys() | acceptable_extra_attrs)
|
||||||
self.assertLessEqual(
|
self.assertLessEqual(
|
||||||
|
|
|
@ -1889,6 +1889,7 @@ _SPECIAL_NAMES = frozenset({
|
||||||
'__init__', '__module__', '__new__', '__slots__',
|
'__init__', '__module__', '__new__', '__slots__',
|
||||||
'__subclasshook__', '__weakref__', '__class_getitem__',
|
'__subclasshook__', '__weakref__', '__class_getitem__',
|
||||||
'__match_args__', '__static_attributes__', '__firstlineno__',
|
'__match_args__', '__static_attributes__', '__firstlineno__',
|
||||||
|
'__annotate__',
|
||||||
})
|
})
|
||||||
|
|
||||||
# These special attributes will be not collected as protocol members.
|
# These special attributes will be not collected as protocol members.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add an ``__annotate__`` attribute to functions, classes, and modules as part
|
||||||
|
of :pep:`649`. Patch by Jelle Zijlstra.
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
|
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
|
||||||
|
#include "pycore_long.h" // _PyLong_GetOne()
|
||||||
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
|
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
|
||||||
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
|
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
|
||||||
#include "pycore_pyerrors.h" // _PyErr_Occurred()
|
#include "pycore_pyerrors.h" // _PyErr_Occurred()
|
||||||
|
@ -124,6 +125,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr)
|
||||||
op->func_weakreflist = NULL;
|
op->func_weakreflist = NULL;
|
||||||
op->func_module = module;
|
op->func_module = module;
|
||||||
op->func_annotations = NULL;
|
op->func_annotations = NULL;
|
||||||
|
op->func_annotate = NULL;
|
||||||
op->func_typeparams = NULL;
|
op->func_typeparams = NULL;
|
||||||
op->vectorcall = _PyFunction_Vectorcall;
|
op->vectorcall = _PyFunction_Vectorcall;
|
||||||
op->func_version = 0;
|
op->func_version = 0;
|
||||||
|
@ -202,6 +204,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
|
||||||
op->func_weakreflist = NULL;
|
op->func_weakreflist = NULL;
|
||||||
op->func_module = module;
|
op->func_module = module;
|
||||||
op->func_annotations = NULL;
|
op->func_annotations = NULL;
|
||||||
|
op->func_annotate = NULL;
|
||||||
op->func_typeparams = NULL;
|
op->func_typeparams = NULL;
|
||||||
op->vectorcall = _PyFunction_Vectorcall;
|
op->vectorcall = _PyFunction_Vectorcall;
|
||||||
op->func_version = 0;
|
op->func_version = 0;
|
||||||
|
@ -512,7 +515,22 @@ static PyObject *
|
||||||
func_get_annotation_dict(PyFunctionObject *op)
|
func_get_annotation_dict(PyFunctionObject *op)
|
||||||
{
|
{
|
||||||
if (op->func_annotations == NULL) {
|
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)) {
|
if (PyTuple_CheckExact(op->func_annotations)) {
|
||||||
PyObject *ann_tuple = op->func_annotations;
|
PyObject *ann_tuple = op->func_annotations;
|
||||||
|
@ -565,7 +583,9 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
|
||||||
"non-dict annotations");
|
"non-dict annotations");
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,10 +783,44 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 *
|
static PyObject *
|
||||||
func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored))
|
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();
|
op->func_annotations = PyDict_New();
|
||||||
if (op->func_annotations == NULL)
|
if (op->func_annotations == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -789,6 +843,7 @@ func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(igno
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_XSETREF(op->func_annotations, Py_XNewRef(value));
|
Py_XSETREF(op->func_annotations, Py_XNewRef(value));
|
||||||
|
Py_CLEAR(op->func_annotate);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,6 +891,7 @@ static PyGetSetDef func_getsetlist[] = {
|
||||||
(setter)func_set_kwdefaults},
|
(setter)func_set_kwdefaults},
|
||||||
{"__annotations__", (getter)func_get_annotations,
|
{"__annotations__", (getter)func_get_annotations,
|
||||||
(setter)func_set_annotations},
|
(setter)func_set_annotations},
|
||||||
|
{"__annotate__", (getter)func_get_annotate, (setter)func_set_annotate},
|
||||||
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
|
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
|
||||||
{"__name__", (getter)func_get_name, (setter)func_set_name},
|
{"__name__", (getter)func_get_name, (setter)func_set_name},
|
||||||
{"__qualname__", (getter)func_get_qualname, (setter)func_set_qualname},
|
{"__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_dict);
|
||||||
Py_CLEAR(op->func_closure);
|
Py_CLEAR(op->func_closure);
|
||||||
Py_CLEAR(op->func_annotations);
|
Py_CLEAR(op->func_annotations);
|
||||||
|
Py_CLEAR(op->func_annotate);
|
||||||
Py_CLEAR(op->func_typeparams);
|
Py_CLEAR(op->func_typeparams);
|
||||||
// Don't Py_CLEAR(op->func_code), since code is always required
|
// Don't Py_CLEAR(op->func_code), since code is always required
|
||||||
// to be non-NULL. Similarly, name and qualname shouldn't be NULL.
|
// 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_dict);
|
||||||
Py_VISIT(f->func_closure);
|
Py_VISIT(f->func_closure);
|
||||||
Py_VISIT(f->func_annotations);
|
Py_VISIT(f->func_annotations);
|
||||||
|
Py_VISIT(f->func_annotate);
|
||||||
Py_VISIT(f->func_typeparams);
|
Py_VISIT(f->func_typeparams);
|
||||||
Py_VISIT(f->func_qualname);
|
Py_VISIT(f->func_qualname);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
||||||
#include "pycore_fileutils.h" // _Py_wgetcwd
|
#include "pycore_fileutils.h" // _Py_wgetcwd
|
||||||
#include "pycore_interp.h" // PyInterpreterState.importlib
|
#include "pycore_interp.h" // PyInterpreterState.importlib
|
||||||
|
#include "pycore_long.h" // _PyLong_GetOne()
|
||||||
#include "pycore_modsupport.h" // _PyModule_CreateInitialized()
|
#include "pycore_modsupport.h" // _PyModule_CreateInitialized()
|
||||||
#include "pycore_moduleobject.h" // _PyModule_GetDef()
|
#include "pycore_moduleobject.h" // _PyModule_GetDef()
|
||||||
#include "pycore_object.h" // _PyType_AllocNoTrack
|
#include "pycore_object.h" // _PyType_AllocNoTrack
|
||||||
|
@ -1133,7 +1134,7 @@ static PyMethodDef module_methods[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyObject *
|
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__));
|
PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__));
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
|
@ -1144,10 +1145,97 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored))
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
return NULL;
|
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;
|
PyObject *annotations;
|
||||||
if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) == 0) {
|
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) {
|
if (annotations) {
|
||||||
int result = PyDict_SetItem(
|
int result = PyDict_SetItem(
|
||||||
dict, &_Py_ID(__annotations__), annotations);
|
dict, &_Py_ID(__annotations__), annotations);
|
||||||
|
@ -1164,14 +1252,10 @@ static int
|
||||||
module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignored))
|
module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignored))
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__));
|
PyObject *dict = module_get_dict(m);
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (!PyDict_Check(dict)) {
|
|
||||||
PyErr_Format(PyExc_TypeError, "<module>.__dict__ is not a dictionary");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
/* set */
|
/* set */
|
||||||
|
@ -1188,8 +1272,10 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ret == 0 && PyDict_Pop(dict, &_Py_ID(__annotate__), NULL) < 0) {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
exit:
|
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1197,6 +1283,7 @@ exit:
|
||||||
|
|
||||||
static PyGetSetDef module_getsets[] = {
|
static PyGetSetDef module_getsets[] = {
|
||||||
{"__annotations__", (getter)module_get_annotations, (setter)module_set_annotations},
|
{"__annotations__", (getter)module_get_annotations, (setter)module_set_annotations},
|
||||||
|
{"__annotate__", (getter)module_get_annotate, (setter)module_set_annotate},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "pycore_dict.h" // _PyDict_KeysSize()
|
#include "pycore_dict.h" // _PyDict_KeysSize()
|
||||||
#include "pycore_frame.h" // _PyInterpreterFrame
|
#include "pycore_frame.h" // _PyInterpreterFrame
|
||||||
#include "pycore_lock.h" // _PySeqLock_*
|
#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_memoryobject.h" // _PyMemoryView_FromBufferProc()
|
||||||
#include "pycore_modsupport.h" // _PyArg_NoKwnames()
|
#include "pycore_modsupport.h" // _PyArg_NoKwnames()
|
||||||
#include "pycore_moduleobject.h" // _PyModule_GetDef()
|
#include "pycore_moduleobject.h" // _PyModule_GetDef()
|
||||||
|
@ -1674,6 +1674,76 @@ type_set_doc(PyTypeObject *type, PyObject *value, void *context)
|
||||||
return PyDict_SetItem(dict, &_Py_ID(__doc__), value);
|
return PyDict_SetItem(dict, &_Py_ID(__doc__), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 *
|
static PyObject *
|
||||||
type_get_annotations(PyTypeObject *type, void *context)
|
type_get_annotations(PyTypeObject *type, void *context)
|
||||||
{
|
{
|
||||||
|
@ -1683,8 +1753,9 @@ type_get_annotations(PyTypeObject *type, void *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *annotations;
|
PyObject *annotations;
|
||||||
PyObject *dict = lookup_tp_dict(type);
|
PyObject *dict = PyType_GetDict(type);
|
||||||
if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) < 0) {
|
if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) < 0) {
|
||||||
|
Py_DECREF(dict);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (annotations) {
|
if (annotations) {
|
||||||
|
@ -1694,7 +1765,32 @@ type_get_annotations(PyTypeObject *type, void *context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
if (annotations) {
|
||||||
int result = PyDict_SetItem(
|
int result = PyDict_SetItem(
|
||||||
dict, &_Py_ID(__annotations__), annotations);
|
dict, &_Py_ID(__annotations__), annotations);
|
||||||
|
@ -1705,6 +1801,7 @@ type_get_annotations(PyTypeObject *type, void *context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Py_DECREF(dict);
|
||||||
return annotations;
|
return annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1719,7 +1816,7 @@ type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
PyObject *dict = lookup_tp_dict(type);
|
PyObject *dict = PyType_GetDict(type);
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
/* set */
|
/* set */
|
||||||
result = PyDict_SetItem(dict, &_Py_ID(__annotations__), value);
|
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);
|
result = PyDict_Pop(dict, &_Py_ID(__annotations__), NULL);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
PyErr_SetString(PyExc_AttributeError, "__annotations__");
|
PyErr_SetString(PyExc_AttributeError, "__annotations__");
|
||||||
|
Py_DECREF(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
|
Py_DECREF(dict);
|
||||||
return -1;
|
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);
|
PyType_Modified(type);
|
||||||
|
Py_DECREF(dict);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1811,6 +1917,7 @@ static PyGetSetDef type_getsets[] = {
|
||||||
{"__doc__", (getter)type_get_doc, (setter)type_set_doc, NULL},
|
{"__doc__", (getter)type_get_doc, (setter)type_set_doc, NULL},
|
||||||
{"__text_signature__", (getter)type_get_text_signature, NULL, NULL},
|
{"__text_signature__", (getter)type_get_text_signature, NULL, NULL},
|
||||||
{"__annotations__", (getter)type_get_annotations, (setter)type_set_annotations, 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},
|
{"__type_params__", (getter)type_get_type_params, (setter)type_set_type_params, NULL},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue