Skip to content

Commit 4b955a8

Browse files
committed
Support functools.partialmethod annotate functions
1 parent 2e4b927 commit 4b955a8

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

Lib/annotationlib.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,7 @@ def _get_annotate_attr(annotate, attr, default):
905905
(functools := sys.modules.get("functools", None))
906906
and isinstance(annotate, functools.partial)
907907
):
908-
return getattr(annotate.func, attr, default)
908+
return _get_annotate_attr(annotate.func, attr, default)
909909

910910
return default
911911

@@ -929,6 +929,9 @@ def _direct_call_annotate(func, annotate, format):
929929
# We could call the function directly, but then we'd have to handle placeholders,
930930
# and this way should be more robust for future changes.
931931
if isinstance(annotate, functools.partial):
932+
# Partial methods
933+
if self := getattr(annotate, "__self__", None):
934+
return functools.partial(func, self, *annotate.args, **annotate.keywords)(format)
932935
return functools.partial(func, *annotate.args, **annotate.keywords)(format)
933936

934937
# If annotate is a cached function, we've now updated the function data, so

Lib/test/test_annotationlib.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,6 +1648,31 @@ def format(format, second, /, *, third, __Format=Format,
16481648

16491649
self.assertEqual(annotations, {"x": Format.VALUE * 5 * 6})
16501650

1651+
def test_callable_partialmethod_annotate_forwardref_value_fallback(self):
1652+
# If Format.STRING and Format.VALUE_WITH_FAKE_GLOBALS are not
1653+
# supported fall back to Format.VALUE and convert to strings
1654+
class Annotate:
1655+
def _internal_format(self, format, second, /, *, third, __Format=Format,
1656+
__NotImplementedError=NotImplementedError):
1657+
if format == __Format.VALUE:
1658+
return {"x": format * second * third}
1659+
else:
1660+
raise __NotImplementedError(format)
1661+
1662+
format = functools.partialmethod(
1663+
_internal_format,
1664+
functools.Placeholder,
1665+
5,
1666+
third=6
1667+
)
1668+
1669+
annotations = annotationlib.call_annotate_function(
1670+
Annotate().format,
1671+
Format.FORWARDREF,
1672+
)
1673+
1674+
self.assertEqual(annotations, {"x": Format.VALUE * 5 * 6})
1675+
16511676
def test_callable_cache_annotate_forwardref_value_fallback(self):
16521677
# If Format.STRING and Format.VALUE_WITH_FAKE_GLOBALS are not
16531678
# supported fall back to Format.VALUE and convert to strings

0 commit comments

Comments
 (0)