mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-119180: Make FORWARDREF format look at __annotations__ first (#124479)
From discussion with Larry Hastings and Carl Meyer, this is the desired behavior.
This commit is contained in:
parent
4e2fb7bdf5
commit
bc543936ab
2 changed files with 141 additions and 24 deletions
|
@ -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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue