Skip to content

Commit a76d794

Browse files
committed
Improve error message when __code__ attribute is not found on annotate function
1 parent b749c49 commit a76d794

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

Lib/annotationlib.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
728728
annotate, owner, is_class, globals, allow_evaluation=False
729729
)
730730
func = types.FunctionType(
731-
_get_annotate_attr(annotate, "__code__", None),
731+
_get_annotate_attr(annotate, "__code__"),
732732
globals,
733733
closure=closure,
734734
argdefs=_get_annotate_attr(annotate, "__defaults__", None),
@@ -763,7 +763,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
763763
# Grab and store all the annotate function attributes that we might need to access
764764
# multiple times as variables, as this could be a bit expensive for non-functions.
765765
annotate_globals = _get_annotate_attr(annotate, "__globals__", {})
766-
annotate_code = _get_annotate_attr(annotate, "__code__", None)
766+
annotate_code = _get_annotate_attr(annotate, "__code__")
767767
annotate_defaults = _get_annotate_attr(annotate, "__defaults__", None)
768768
annotate_kwdefaults = _get_annotate_attr(annotate, "__kwdefaults__", None)
769769
namespace = {
@@ -890,7 +890,7 @@ def _stringify_single(anno):
890890
return repr(anno)
891891

892892

893-
def _get_annotate_attr(annotate, attr, default):
893+
def _get_annotate_attr(annotate, attr, default=_sentinel):
894894
# Try to get the attr on the annotate function. If it doesn't exist, we might
895895
# need to look in other places on the object. If all of those fail, we can
896896
# return the default at the end.
@@ -944,6 +944,8 @@ def _get_annotate_attr(annotate, attr, default):
944944
):
945945
return _get_annotate_attr(annotate.func, attr, default)
946946

947+
if default is _sentinel:
948+
raise TypeError(f"annotate function missing {attr!r} attribute")
947949
return default
948950

949951
def _direct_call_annotate(func, annotate, *args):

Lib/test/test_annotationlib.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,31 @@ def __call__(self, format, /):
21192119
with self.assertRaises(DemoException):
21202120
annotationlib.call_annotate_function(Annotate(), format=fmt)
21212121

2122+
def test_unsupported_callable_object_fakeglobals_error(self):
2123+
# Test that a readable error is raised when an unsupported callable
2124+
# type is used as an annotate function with fake globals.
2125+
2126+
def annotate(format, /, __Format=Format,
2127+
__NotImplementedError=NotImplementedError):
2128+
if format == __Format.VALUE:
2129+
return {"x": int}
2130+
elif format == __Format.VALUE_WITH_FAKE_GLOBALS:
2131+
return {"x": str}
2132+
else:
2133+
raise __NotImplementedError(format)
2134+
2135+
annotations = annotationlib.call_annotate_function(
2136+
annotate.__call__,
2137+
Format.VALUE
2138+
)
2139+
self.assertEqual(annotations, {"x": int})
2140+
2141+
for fmt in (Format.FORWARDREF, Format.STRING):
2142+
with self.assertRaisesRegex(
2143+
TypeError, "annotate function missing '__code__' attribute"
2144+
):
2145+
annotationlib.call_annotate_function(annotate.__call__, fmt)
2146+
21222147

21232148
class MetaclassTests(unittest.TestCase):
21242149
def test_annotated_meta(self):

0 commit comments

Comments
 (0)