From e9d1360c9a1072aa23950b321491dc542c3a19b8 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Fri, 24 Nov 2023 10:46:08 +0100 Subject: [PATCH] gh-112345: `typing.Protocol`: Let failed subclasscheck show non-method members (#112344) Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 16 ++++++++++++++++ Lib/typing.py | 7 ++++++- ...023-11-23-17-25-27.gh-issue-112345.FFApHx.rst | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2b5f34b4b92e0c..31d7fda2f978da 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4091,6 +4091,22 @@ def method(self) -> None: ... self.assertIsInstance(Foo(), ProtocolWithMixedMembers) self.assertNotIsInstance(42, ProtocolWithMixedMembers) + def test_protocol_issubclass_error_message(self): + class Vec2D(Protocol): + x: float + y: float + + def square_norm(self) -> float: + return self.x ** 2 + self.y ** 2 + + self.assertEqual(Vec2D.__protocol_attrs__, {'x', 'y', 'square_norm'}) + expected_error_message = ( + "Protocols with non-method members don't support issubclass()." + " Non-method members: 'x', 'y'." + ) + with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)): + issubclass(int, Vec2D) + class GenericTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index a96c7083eb785e..872aca82c4e779 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1828,8 +1828,13 @@ def __subclasscheck__(cls, other): not cls.__callable_proto_members_only__ and cls.__dict__.get("__subclasshook__") is _proto_hook ): + non_method_attrs = sorted( + attr for attr in cls.__protocol_attrs__ + if not callable(getattr(cls, attr, None)) + ) raise TypeError( - "Protocols with non-method members don't support issubclass()" + "Protocols with non-method members don't support issubclass()." + f" Non-method members: {str(non_method_attrs)[1:-1]}." ) if not getattr(cls, '_is_runtime_protocol', False): raise TypeError( diff --git a/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst b/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst new file mode 100644 index 00000000000000..b2b9894e6bef3a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst @@ -0,0 +1,3 @@ +Improve error message when trying to call :func:`issubclass` against a +:class:`typing.Protocol` that has non-method members. +Patch by Randolf Scholz.