diff options
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/ann_module4.py | 5 | ||||
-rw-r--r-- | Lib/test/test_grammar.py | 3 | ||||
-rw-r--r-- | Lib/test/test_module.py | 54 | ||||
-rw-r--r-- | Lib/test/test_opcodes.py | 5 | ||||
-rw-r--r-- | Lib/test/test_type_annotations.py | 103 |
5 files changed, 165 insertions, 5 deletions
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 GrammarTests(unittest.TestCase): 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 @@ a = A(destroyed)""" 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 @@ class OpcodeTest(unittest.TestCase): 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__, {}) |