mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	bpo-43901: Lazy-create an empty annotations dict in all unannotated user classes and modules (#25623)
Change class and module objects to lazy-create empty annotations dicts on demand. The annotations dicts are stored in the object's `__dict__` for backwards compatibility.
This commit is contained in:
		
							parent
							
								
									dbe60ee09d
								
							
						
					
					
						commit
						2f2b69855d
					
				
					 9 changed files with 308 additions and 8 deletions
				
			
		
							
								
								
									
										5
									
								
								Lib/test/ann_module4.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Lib/test/ann_module4.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					# This ann_module isn't for test_typing,
 | 
				
			||||||
 | 
					# it's for test_module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a:int=3
 | 
				
			||||||
 | 
					b:str=4
 | 
				
			||||||
| 
						 | 
					@ -382,8 +382,7 @@ class CC(metaclass=CMeta):
 | 
				
			||||||
        self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
 | 
					        self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_var_annot_module_semantics(self):
 | 
					    def test_var_annot_module_semantics(self):
 | 
				
			||||||
        with self.assertRaises(AttributeError):
 | 
					        self.assertEqual(test.__annotations__, {})
 | 
				
			||||||
            print(test.__annotations__)
 | 
					 | 
				
			||||||
        self.assertEqual(ann_module.__annotations__,
 | 
					        self.assertEqual(ann_module.__annotations__,
 | 
				
			||||||
                     {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]})
 | 
					                     {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]})
 | 
				
			||||||
        self.assertEqual(ann_module.M.__annotations__,
 | 
					        self.assertEqual(ann_module.M.__annotations__,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -286,6 +286,60 @@ class M(ModuleType):
 | 
				
			||||||
            melon = Descr()
 | 
					            melon = Descr()
 | 
				
			||||||
        self.assertRaises(RuntimeError, getattr, M("mymod"), "melon")
 | 
					        self.assertRaises(RuntimeError, getattr, M("mymod"), "melon")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_lazy_create_annotations(self):
 | 
				
			||||||
 | 
					        # module objects lazy create their __annotations__ dict on demand.
 | 
				
			||||||
 | 
					        # the annotations dict is stored in module.__dict__.
 | 
				
			||||||
 | 
					        # a freshly created module shouldn't have an annotations dict yet.
 | 
				
			||||||
 | 
					        foo = ModuleType("foo")
 | 
				
			||||||
 | 
					        for i in range(4):
 | 
				
			||||||
 | 
					            self.assertFalse("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            d = foo.__annotations__
 | 
				
			||||||
 | 
					            self.assertTrue("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__annotations__, d)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__dict__['__annotations__'], d)
 | 
				
			||||||
 | 
					            if i % 2:
 | 
				
			||||||
 | 
					                del foo.__annotations__
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                del foo.__dict__['__annotations__']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_setting_annotations(self):
 | 
				
			||||||
 | 
					        foo = ModuleType("foo")
 | 
				
			||||||
 | 
					        for i in range(4):
 | 
				
			||||||
 | 
					            self.assertFalse("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            d = {'a': int}
 | 
				
			||||||
 | 
					            foo.__annotations__ = d
 | 
				
			||||||
 | 
					            self.assertTrue("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__annotations__, d)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__dict__['__annotations__'], d)
 | 
				
			||||||
 | 
					            if i % 2:
 | 
				
			||||||
 | 
					                del foo.__annotations__
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                del foo.__dict__['__annotations__']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_annotations_getset_raises(self):
 | 
				
			||||||
 | 
					        # module has no dict, all operations fail
 | 
				
			||||||
 | 
					        foo = ModuleType.__new__(ModuleType)
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError):
 | 
				
			||||||
 | 
					            print(foo.__annotations__)
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError):
 | 
				
			||||||
 | 
					            foo.__annotations__ = {}
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError):
 | 
				
			||||||
 | 
					            del foo.__annotations__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # double delete
 | 
				
			||||||
 | 
					        foo = ModuleType("foo")
 | 
				
			||||||
 | 
					        foo.__annotations__ = {}
 | 
				
			||||||
 | 
					        del foo.__annotations__
 | 
				
			||||||
 | 
					        with self.assertRaises(AttributeError):
 | 
				
			||||||
 | 
					            del foo.__annotations__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_annotations_are_created_correctly(self):
 | 
				
			||||||
 | 
					        from test import ann_module4
 | 
				
			||||||
 | 
					        self.assertTrue("__annotations__" in ann_module4.__dict__)
 | 
				
			||||||
 | 
					        del ann_module4.__annotations__
 | 
				
			||||||
 | 
					        self.assertFalse("__annotations__" in ann_module4.__dict__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # frozen and namespace module reprs are tested in importlib.
 | 
					    # frozen and namespace module reprs are tested in importlib.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,10 +31,9 @@ def test_setup_annotations_line(self):
 | 
				
			||||||
        except OSError:
 | 
					        except OSError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_no_annotations_if_not_needed(self):
 | 
					    def test_default_annotations_exist(self):
 | 
				
			||||||
        class C: pass
 | 
					        class C: pass
 | 
				
			||||||
        with self.assertRaises(AttributeError):
 | 
					        self.assertEqual(C.__annotations__, {})
 | 
				
			||||||
            C.__annotations__
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_use_existing_annotations(self):
 | 
					    def test_use_existing_annotations(self):
 | 
				
			||||||
        ns = {'__annotations__': {1: 2}}
 | 
					        ns = {'__annotations__': {1: 2}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										103
									
								
								Lib/test/test_type_annotations.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								Lib/test/test_type_annotations.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,103 @@
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TypeAnnotationTests(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_lazy_create_annotations(self):
 | 
				
			||||||
 | 
					        # type objects lazy create their __annotations__ dict on demand.
 | 
				
			||||||
 | 
					        # the annotations dict is stored in type.__dict__.
 | 
				
			||||||
 | 
					        # a freshly created type shouldn't have an annotations dict yet.
 | 
				
			||||||
 | 
					        foo = type("Foo", (), {})
 | 
				
			||||||
 | 
					        for i in range(3):
 | 
				
			||||||
 | 
					            self.assertFalse("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            d = foo.__annotations__
 | 
				
			||||||
 | 
					            self.assertTrue("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__annotations__, d)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__dict__['__annotations__'], d)
 | 
				
			||||||
 | 
					            del foo.__annotations__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_setting_annotations(self):
 | 
				
			||||||
 | 
					        foo = type("Foo", (), {})
 | 
				
			||||||
 | 
					        for i in range(3):
 | 
				
			||||||
 | 
					            self.assertFalse("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            d = {'a': int}
 | 
				
			||||||
 | 
					            foo.__annotations__ = d
 | 
				
			||||||
 | 
					            self.assertTrue("__annotations__" in foo.__dict__)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__annotations__, d)
 | 
				
			||||||
 | 
					            self.assertEqual(foo.__dict__['__annotations__'], d)
 | 
				
			||||||
 | 
					            del foo.__annotations__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_annotations_getset_raises(self):
 | 
				
			||||||
 | 
					        # builtin types don't have __annotations__ (yet!)
 | 
				
			||||||
 | 
					        with self.assertRaises(AttributeError):
 | 
				
			||||||
 | 
					            print(float.__annotations__)
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError):
 | 
				
			||||||
 | 
					            float.__annotations__ = {}
 | 
				
			||||||
 | 
					        with self.assertRaises(TypeError):
 | 
				
			||||||
 | 
					            del float.__annotations__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # double delete
 | 
				
			||||||
 | 
					        foo = type("Foo", (), {})
 | 
				
			||||||
 | 
					        foo.__annotations__ = {}
 | 
				
			||||||
 | 
					        del foo.__annotations__
 | 
				
			||||||
 | 
					        with self.assertRaises(AttributeError):
 | 
				
			||||||
 | 
					            del foo.__annotations__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_annotations_are_created_correctly(self):
 | 
				
			||||||
 | 
					        class C:
 | 
				
			||||||
 | 
					            a:int=3
 | 
				
			||||||
 | 
					            b:str=4
 | 
				
			||||||
 | 
					        self.assertTrue("__annotations__" in C.__dict__)
 | 
				
			||||||
 | 
					        del C.__annotations__
 | 
				
			||||||
 | 
					        self.assertFalse("__annotations__" in C.__dict__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_descriptor_still_works(self):
 | 
				
			||||||
 | 
					        class C:
 | 
				
			||||||
 | 
					            def __init__(self, name=None, bases=None, d=None):
 | 
				
			||||||
 | 
					                self.my_annotations = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @property
 | 
				
			||||||
 | 
					            def __annotations__(self):
 | 
				
			||||||
 | 
					                if not hasattr(self, 'my_annotations'):
 | 
				
			||||||
 | 
					                    self.my_annotations = {}
 | 
				
			||||||
 | 
					                if not isinstance(self.my_annotations, dict):
 | 
				
			||||||
 | 
					                    self.my_annotations = {}
 | 
				
			||||||
 | 
					                return self.my_annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @__annotations__.setter
 | 
				
			||||||
 | 
					            def __annotations__(self, value):
 | 
				
			||||||
 | 
					                if not isinstance(value, dict):
 | 
				
			||||||
 | 
					                    raise ValueError("can only set __annotations__ to a dict")
 | 
				
			||||||
 | 
					                self.my_annotations = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @__annotations__.deleter
 | 
				
			||||||
 | 
					            def __annotations__(self):
 | 
				
			||||||
 | 
					                if hasattr(self, 'my_annotations') and self.my_annotations == None:
 | 
				
			||||||
 | 
					                    raise AttributeError('__annotations__')
 | 
				
			||||||
 | 
					                self.my_annotations = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        c = C()
 | 
				
			||||||
 | 
					        self.assertEqual(c.__annotations__, {})
 | 
				
			||||||
 | 
					        d = {'a':'int'}
 | 
				
			||||||
 | 
					        c.__annotations__ = d
 | 
				
			||||||
 | 
					        self.assertEqual(c.__annotations__, d)
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError):
 | 
				
			||||||
 | 
					            c.__annotations__ = 123
 | 
				
			||||||
 | 
					        del c.__annotations__
 | 
				
			||||||
 | 
					        with self.assertRaises(AttributeError):
 | 
				
			||||||
 | 
					            del c.__annotations__
 | 
				
			||||||
 | 
					        self.assertEqual(c.__annotations__, {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class D(metaclass=C):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(D.__annotations__, {})
 | 
				
			||||||
 | 
					        d = {'a':'int'}
 | 
				
			||||||
 | 
					        D.__annotations__ = d
 | 
				
			||||||
 | 
					        self.assertEqual(D.__annotations__, d)
 | 
				
			||||||
 | 
					        with self.assertRaises(ValueError):
 | 
				
			||||||
 | 
					            D.__annotations__ = 123
 | 
				
			||||||
 | 
					        del D.__annotations__
 | 
				
			||||||
 | 
					        with self.assertRaises(AttributeError):
 | 
				
			||||||
 | 
					            del D.__annotations__
 | 
				
			||||||
 | 
					        self.assertEqual(D.__annotations__, {})
 | 
				
			||||||
| 
						 | 
					@ -1677,6 +1677,8 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                base_globals = globalns
 | 
					                base_globals = globalns
 | 
				
			||||||
            ann = base.__dict__.get('__annotations__', {})
 | 
					            ann = base.__dict__.get('__annotations__', {})
 | 
				
			||||||
 | 
					            if isinstance(ann, types.GetSetDescriptorType):
 | 
				
			||||||
 | 
					                ann = {}
 | 
				
			||||||
            base_locals = dict(vars(base)) if localns is None else localns
 | 
					            base_locals = dict(vars(base)) if localns is None else localns
 | 
				
			||||||
            if localns is None and globalns is None:
 | 
					            if localns is None and globalns is None:
 | 
				
			||||||
                # This is surprising, but required.  Before Python 3.10,
 | 
					                # This is surprising, but required.  Before Python 3.10,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					Change class and module objects to lazy-create empty annotations dicts on
 | 
				
			||||||
 | 
					demand.  The annotations dicts are stored in the object's __dict__ for
 | 
				
			||||||
 | 
					backwards compatibility.
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,9 @@ static Py_ssize_t max_module_number;
 | 
				
			||||||
_Py_IDENTIFIER(__doc__);
 | 
					_Py_IDENTIFIER(__doc__);
 | 
				
			||||||
_Py_IDENTIFIER(__name__);
 | 
					_Py_IDENTIFIER(__name__);
 | 
				
			||||||
_Py_IDENTIFIER(__spec__);
 | 
					_Py_IDENTIFIER(__spec__);
 | 
				
			||||||
 | 
					_Py_IDENTIFIER(__dict__);
 | 
				
			||||||
 | 
					_Py_IDENTIFIER(__dir__);
 | 
				
			||||||
 | 
					_Py_IDENTIFIER(__annotations__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static PyMemberDef module_members[] = {
 | 
					static PyMemberDef module_members[] = {
 | 
				
			||||||
    {"__dict__", T_OBJECT, offsetof(PyModuleObject, md_dict), READONLY},
 | 
					    {"__dict__", T_OBJECT, offsetof(PyModuleObject, md_dict), READONLY},
 | 
				
			||||||
| 
						 | 
					@ -807,8 +810,6 @@ module_clear(PyModuleObject *m)
 | 
				
			||||||
static PyObject *
 | 
					static PyObject *
 | 
				
			||||||
module_dir(PyObject *self, PyObject *args)
 | 
					module_dir(PyObject *self, PyObject *args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    _Py_IDENTIFIER(__dict__);
 | 
					 | 
				
			||||||
    _Py_IDENTIFIER(__dir__);
 | 
					 | 
				
			||||||
    PyObject *result = NULL;
 | 
					    PyObject *result = NULL;
 | 
				
			||||||
    PyObject *dict = _PyObject_GetAttrId(self, &PyId___dict__);
 | 
					    PyObject *dict = _PyObject_GetAttrId(self, &PyId___dict__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -841,6 +842,71 @@ static PyMethodDef module_methods[] = {
 | 
				
			||||||
    {0}
 | 
					    {0}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored))
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PyObject *dict = _PyObject_GetAttrId((PyObject *)m, &PyId___dict__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((dict == NULL) || !PyDict_Check(dict)) {
 | 
				
			||||||
 | 
					        PyErr_Format(PyExc_TypeError, "<module>.__dict__ is not a dictionary");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PyObject *annotations;
 | 
				
			||||||
 | 
					    /* there's no _PyDict_GetItemId without WithError, so let's LBYL. */
 | 
				
			||||||
 | 
					    if (_PyDict_ContainsId(dict, &PyId___annotations__)) {
 | 
				
			||||||
 | 
					        annotations = _PyDict_GetItemIdWithError(dict, &PyId___annotations__);
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					        ** _PyDict_GetItemIdWithError could still fail,
 | 
				
			||||||
 | 
					        ** for instance with a well-timed Ctrl-C or a MemoryError.
 | 
				
			||||||
 | 
					        ** so let's be totally safe.
 | 
				
			||||||
 | 
					        */
 | 
				
			||||||
 | 
					        if (annotations) {
 | 
				
			||||||
 | 
					            Py_INCREF(annotations);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        annotations = PyDict_New();
 | 
				
			||||||
 | 
					        if (annotations) {
 | 
				
			||||||
 | 
					            int result = _PyDict_SetItemId(dict, &PyId___annotations__, annotations);
 | 
				
			||||||
 | 
					            if (result) {
 | 
				
			||||||
 | 
					                Py_CLEAR(annotations);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Py_DECREF(dict);
 | 
				
			||||||
 | 
					    return annotations;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignored))
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PyObject *dict = _PyObject_GetAttrId((PyObject *)m, &PyId___dict__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((dict == NULL) || !PyDict_Check(dict)) {
 | 
				
			||||||
 | 
					        PyErr_Format(PyExc_TypeError, "<module>.__dict__ is not a dictionary");
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (value != NULL) {
 | 
				
			||||||
 | 
					        /* set */
 | 
				
			||||||
 | 
					        return _PyDict_SetItemId(dict, &PyId___annotations__, value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* delete */
 | 
				
			||||||
 | 
					    if (!_PyDict_ContainsId(dict, &PyId___annotations__)) {
 | 
				
			||||||
 | 
					        PyErr_Format(PyExc_AttributeError, "__annotations__");
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return _PyDict_DelItemId(dict, &PyId___annotations__);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyGetSetDef module_getsets[] = {
 | 
				
			||||||
 | 
					    {"__annotations__", (getter)module_get_annotations, (setter)module_set_annotations},
 | 
				
			||||||
 | 
					    {NULL}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyTypeObject PyModule_Type = {
 | 
					PyTypeObject PyModule_Type = {
 | 
				
			||||||
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
 | 
					    PyVarObject_HEAD_INIT(&PyType_Type, 0)
 | 
				
			||||||
    "module",                                   /* tp_name */
 | 
					    "module",                                   /* tp_name */
 | 
				
			||||||
| 
						 | 
					@ -872,7 +938,7 @@ PyTypeObject PyModule_Type = {
 | 
				
			||||||
    0,                                          /* tp_iternext */
 | 
					    0,                                          /* tp_iternext */
 | 
				
			||||||
    module_methods,                             /* tp_methods */
 | 
					    module_methods,                             /* tp_methods */
 | 
				
			||||||
    module_members,                             /* tp_members */
 | 
					    module_members,                             /* tp_members */
 | 
				
			||||||
    0,                                          /* tp_getset */
 | 
					    module_getsets,                             /* tp_getset */
 | 
				
			||||||
    0,                                          /* tp_base */
 | 
					    0,                                          /* tp_base */
 | 
				
			||||||
    0,                                          /* tp_dict */
 | 
					    0,                                          /* tp_dict */
 | 
				
			||||||
    0,                                          /* tp_descr_get */
 | 
					    0,                                          /* tp_descr_get */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,7 @@ typedef struct PySlot_Offset {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* alphabetical order */
 | 
					/* alphabetical order */
 | 
				
			||||||
_Py_IDENTIFIER(__abstractmethods__);
 | 
					_Py_IDENTIFIER(__abstractmethods__);
 | 
				
			||||||
 | 
					_Py_IDENTIFIER(__annotations__);
 | 
				
			||||||
_Py_IDENTIFIER(__class__);
 | 
					_Py_IDENTIFIER(__class__);
 | 
				
			||||||
_Py_IDENTIFIER(__class_getitem__);
 | 
					_Py_IDENTIFIER(__class_getitem__);
 | 
				
			||||||
_Py_IDENTIFIER(__classcell__);
 | 
					_Py_IDENTIFIER(__classcell__);
 | 
				
			||||||
| 
						 | 
					@ -930,6 +931,73 @@ type_set_doc(PyTypeObject *type, PyObject *value, void *context)
 | 
				
			||||||
    return _PyDict_SetItemId(type->tp_dict, &PyId___doc__, value);
 | 
					    return _PyDict_SetItemId(type->tp_dict, &PyId___doc__, value);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					type_get_annotations(PyTypeObject *type, void *context)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
 | 
				
			||||||
 | 
					        PyErr_Format(PyExc_AttributeError, "type object '%s' has no attribute '__annotations__'", type->tp_name);
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PyObject *annotations;
 | 
				
			||||||
 | 
					    /* there's no _PyDict_GetItemId without WithError, so let's LBYL. */
 | 
				
			||||||
 | 
					    if (_PyDict_ContainsId(type->tp_dict, &PyId___annotations__)) {
 | 
				
			||||||
 | 
					        annotations = _PyDict_GetItemIdWithError(type->tp_dict, &PyId___annotations__);
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					        ** _PyDict_GetItemIdWithError could still fail,
 | 
				
			||||||
 | 
					        ** for instance with a well-timed Ctrl-C or a MemoryError.
 | 
				
			||||||
 | 
					        ** so let's be totally safe.
 | 
				
			||||||
 | 
					        */
 | 
				
			||||||
 | 
					        if (annotations) {
 | 
				
			||||||
 | 
					            if (Py_TYPE(annotations)->tp_descr_get) {
 | 
				
			||||||
 | 
					                annotations = Py_TYPE(annotations)->tp_descr_get(annotations, NULL,
 | 
				
			||||||
 | 
					                                                       (PyObject *)type);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Py_INCREF(annotations);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        annotations = PyDict_New();
 | 
				
			||||||
 | 
					        if (annotations) {
 | 
				
			||||||
 | 
					            int result = _PyDict_SetItemId(type->tp_dict, &PyId___annotations__, annotations);
 | 
				
			||||||
 | 
					            if (result) {
 | 
				
			||||||
 | 
					                Py_CLEAR(annotations);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                PyType_Modified(type);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return annotations;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
 | 
				
			||||||
 | 
					        PyErr_Format(PyExc_TypeError, "can't set attributes of built-in/extension type '%s'", type->tp_name);
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int result;
 | 
				
			||||||
 | 
					    if (value != NULL) {
 | 
				
			||||||
 | 
					        /* set */
 | 
				
			||||||
 | 
					        result = _PyDict_SetItemId(type->tp_dict, &PyId___annotations__, value);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* delete */
 | 
				
			||||||
 | 
					        if (!_PyDict_ContainsId(type->tp_dict, &PyId___annotations__)) {
 | 
				
			||||||
 | 
					            PyErr_Format(PyExc_AttributeError, "__annotations__");
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        result = _PyDict_DelItemId(type->tp_dict, &PyId___annotations__);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (result == 0) {
 | 
				
			||||||
 | 
					        PyType_Modified(type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*[clinic input]
 | 
					/*[clinic input]
 | 
				
			||||||
type.__instancecheck__ -> bool
 | 
					type.__instancecheck__ -> bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -973,6 +1041,7 @@ static PyGetSetDef type_getsets[] = {
 | 
				
			||||||
    {"__dict__",  (getter)type_dict,  NULL, NULL},
 | 
					    {"__dict__",  (getter)type_dict,  NULL, NULL},
 | 
				
			||||||
    {"__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},
 | 
				
			||||||
    {0}
 | 
					    {0}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue