diff options
author | Jelle Zijlstra <jelle.zijlstra@gmail.com> | 2024-09-25 22:32:45 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-25 22:32:45 (GMT) |
commit | bc543936ab4ca3625bd7cbeac97faa47f5fd93dc (patch) | |
tree | cbee4a1c567440e846e30663f63e86fcded5b061 /Lib/test | |
parent | 4e2fb7bdf5e1b2eddeb29b776820cfa2017a673f (diff) | |
download | cpython-bc543936ab4ca3625bd7cbeac97faa47f5fd93dc.zip cpython-bc543936ab4ca3625bd7cbeac97faa47f5fd93dc.tar.gz cpython-bc543936ab4ca3625bd7cbeac97faa47f5fd93dc.tar.bz2 |
gh-119180: Make FORWARDREF format look at __annotations__ first (#124479)
From discussion with Larry Hastings and Carl Meyer, this is the desired
behavior.
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test_annotationlib.py | 88 |
1 files changed, 84 insertions, 4 deletions
diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index cc051ef..5b052da 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -740,17 +740,97 @@ class TestGetAnnotations(unittest.TestCase): self.assertEqual(annotationlib.get_annotations(f), {"x": int}) self.assertEqual( - annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF), + annotationlib.get_annotations(f, format=Format.FORWARDREF), {"x": int}, ) f.__annotations__["x"] = str # The modification is reflected in VALUE (the default) self.assertEqual(annotationlib.get_annotations(f), {"x": str}) - # ... but not in FORWARDREF, which uses __annotate__ + # ... and also in FORWARDREF, which tries __annotations__ if available self.assertEqual( - annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF), - {"x": int}, + annotationlib.get_annotations(f, format=Format.FORWARDREF), + {"x": str}, + ) + # ... but not in SOURCE which always uses __annotate__ + self.assertEqual( + annotationlib.get_annotations(f, format=Format.SOURCE), + {"x": "int"}, + ) + + def test_non_dict_annotations(self): + class WeirdAnnotations: + @property + def __annotations__(self): + return "not a dict" + + wa = WeirdAnnotations() + for format in Format: + with ( + self.subTest(format=format), + self.assertRaisesRegex( + ValueError, r".*__annotations__ is neither a dict nor None" + ), + ): + annotationlib.get_annotations(wa, format=format) + + def test_annotations_on_custom_object(self): + class HasAnnotations: + @property + def __annotations__(self): + return {"x": int} + + ha = HasAnnotations() + self.assertEqual( + annotationlib.get_annotations(ha, format=Format.VALUE), {"x": int} + ) + self.assertEqual( + annotationlib.get_annotations(ha, format=Format.FORWARDREF), {"x": int} + ) + + # TODO(gh-124412): This should return {'x': 'int'} instead. + self.assertEqual( + annotationlib.get_annotations(ha, format=Format.SOURCE), {"x": int} + ) + + def test_raising_annotations_on_custom_object(self): + class HasRaisingAnnotations: + @property + def __annotations__(self): + return {"x": undefined} + + hra = HasRaisingAnnotations() + + with self.assertRaises(NameError): + annotationlib.get_annotations(hra, format=Format.VALUE) + + with self.assertRaises(NameError): + annotationlib.get_annotations(hra, format=Format.FORWARDREF) + + undefined = float + self.assertEqual( + annotationlib.get_annotations(hra, format=Format.VALUE), {"x": float} + ) + + def test_forwardref_prefers_annotations(self): + class HasBoth: + @property + def __annotations__(self): + return {"x": int} + + @property + def __annotate__(self): + return lambda format: {"x": str} + + hb = HasBoth() + self.assertEqual( + annotationlib.get_annotations(hb, format=Format.VALUE), {"x": int} + ) + self.assertEqual( + annotationlib.get_annotations(hb, format=Format.FORWARDREF), {"x": int} + ) + self.assertEqual( + annotationlib.get_annotations(hb, format=Format.SOURCE), {"x": str} ) def test_pep695_generic_class_with_future_annotations(self): |