From 87105ac23228132d54560d6e5f75d0938cba704b Mon Sep 17 00:00:00 2001 From: Shardul D Date: Mon, 22 Jun 2026 04:03:19 +0530 Subject: [PATCH] gh-146643: Fix Protocol creation crashing on an __annotate__ member _get_protocol_attrs() collects annotation names via annotationlib when a base's __annotations__ cannot be read directly. When a Protocol member is named __annotate__ (a reserved name) but is an ordinary method rather than a valid annotate function, both annotation-retrieval paths raised TypeError, so merely defining the Protocol failed. Treat a base whose annotations cannot be retrieved as un-annotated instead of propagating the error. --- Lib/test/test_typing.py | 13 +++++++++++++ Lib/typing.py | 13 ++++++++++--- .../2026-06-22-14-15-00.gh-issue-146643.Lm5wTq.rst | 3 +++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-22-14-15-00.gh-issue-146643.Lm5wTq.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 042604ed7c1a423..2725f0089baba29 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4254,6 +4254,19 @@ def meth(self): self.assertEqual(frozenset(typing._get_protocol_attrs(PG)), frozenset({'x', 'meth'})) + def test_protocol_with_annotate_method_does_not_crash(self): + # gh-146643: defining a Protocol whose member is named __annotate__ + # (a reserved name whose value is not a usable annotate function) + # used to crash protocol attribute collection with a TypeError. + class CanAnnotate(Protocol): + def __annotate__(self, format, /): ... + + self.assertIs(CanAnnotate._is_protocol, True) + # __annotate__ is an excluded special name, so it is not collected as + # a protocol member, but creating the class must not raise. + self.assertNotIn('__annotate__', + typing._get_protocol_attrs(CanAnnotate)) + def test_no_runtime_deco_on_nominal(self): with self.assertRaises(TypeError): @runtime_checkable diff --git a/Lib/typing.py b/Lib/typing.py index 1579f492003f748..16227c0f445bd51 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1874,9 +1874,16 @@ def _get_protocol_attrs(cls): annotations = base.__annotations__ except Exception: # Only go through annotationlib to handle deferred annotations if we need to - annotations = annotationlib.get_annotations( - base, format=annotationlib.Format.FORWARDREF - ) + try: + annotations = annotationlib.get_annotations( + base, format=annotationlib.Format.FORWARDREF + ) + except Exception: + # The annotations cannot be retrieved at all, e.g. the base + # defines an ``__annotate__`` member that is not a usable + # annotate function (gh-146643). Treat it as un-annotated + # rather than letting the class definition fail. + annotations = {} for attr in (*base.__dict__, *annotations): if not attr.startswith('_abc_') and attr not in EXCLUDED_ATTRIBUTES: attrs.add(attr) diff --git a/Misc/NEWS.d/next/Library/2026-06-22-14-15-00.gh-issue-146643.Lm5wTq.rst b/Misc/NEWS.d/next/Library/2026-06-22-14-15-00.gh-issue-146643.Lm5wTq.rst new file mode 100644 index 000000000000000..868837df2ac6054 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-22-14-15-00.gh-issue-146643.Lm5wTq.rst @@ -0,0 +1,3 @@ +Fix a :exc:`TypeError` raised when defining a :class:`typing.Protocol` whose +body (or one of its bases) declares a member named ``__annotate__`` that is not +a usable annotate function. Such a class can now be created without error.