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.
diff --git a/Lib/test/ann_module4.py b/Lib/test/ann_module4.py
new file mode 100644
index 0000000..13e9aee
--- /dev/null
+++ b/Lib/test/ann_module4.py
@@ -0,0 +1,5 @@
+# This ann_module isn't for test_typing,
+# it's for test_module
+
+a:int=3
+b:str=4
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 6f79e19..46f70e5 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -382,8 +382,7 @@ class CC(metaclass=CMeta):
         self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
 
     def test_var_annot_module_semantics(self):
-        with self.assertRaises(AttributeError):
-            print(test.__annotations__)
+        self.assertEqual(test.__annotations__, {})
         self.assertEqual(ann_module.__annotations__,
                      {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]})
         self.assertEqual(ann_module.M.__annotations__,
diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py
index 1d44563..aa5ee49 100644
--- a/Lib/test/test_module.py
+++ b/Lib/test/test_module.py
@@ -286,6 +286,60 @@ class M(ModuleType):
             melon = Descr()
         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.
 
 
diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py
index d43a830..e880c3f 100644
--- a/Lib/test/test_opcodes.py
+++ b/Lib/test/test_opcodes.py
@@ -31,10 +31,9 @@ def test_setup_annotations_line(self):
         except OSError:
             pass
 
-    def test_no_annotations_if_not_needed(self):
+    def test_default_annotations_exist(self):
         class C: pass
-        with self.assertRaises(AttributeError):
-            C.__annotations__
+        self.assertEqual(C.__annotations__, {})
 
     def test_use_existing_annotations(self):
         ns = {'__annotations__': {1: 2}}
diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py
new file mode 100644
index 0000000..f6c99bd
--- /dev/null
+++ b/Lib/test/test_type_annotations.py
@@ -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__, {})
diff --git a/Lib/typing.py b/Lib/typing.py
index d409517..ff96434 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -1677,6 +1677,8 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
             else:
                 base_globals = globalns
             ann = base.__dict__.get('__annotations__', {})
+            if isinstance(ann, types.GetSetDescriptorType):
+                ann = {}
             base_locals = dict(vars(base)) if localns is None else localns
             if localns is None and globalns is None:
                 # This is surprising, but required.  Before Python 3.10,