summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorJelle Zijlstra <jelle.zijlstra@gmail.com>2024-09-25 22:32:45 (GMT)
committerGitHub <noreply@github.com>2024-09-25 22:32:45 (GMT)
commitbc543936ab4ca3625bd7cbeac97faa47f5fd93dc (patch)
treecbee4a1c567440e846e30663f63e86fcded5b061 /Lib/test
parent4e2fb7bdf5e1b2eddeb29b776820cfa2017a673f (diff)
downloadcpython-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.py88
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):