]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-112345: `typing.Protocol`: Let failed subclasscheck show non-method members (...
authorRandolf Scholz <randolf.scholz@gmail.com>
Fri, 24 Nov 2023 09:46:08 +0000 (10:46 +0100)
committerGitHub <noreply@github.com>
Fri, 24 Nov 2023 09:46:08 +0000 (09:46 +0000)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst [new file with mode: 0644]

index 2b5f34b4b92e0c90adfb395bc03a5821aa5c53fd..31d7fda2f978da98c7bf5030a56090c3b09e65f5 100644 (file)
@@ -4091,6 +4091,22 @@ class ProtocolTests(BaseTestCase):
         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):
 
index a96c7083eb785e3cf80dbb501cf133a7414a5dbf..872aca82c4e779b7730d561b6f8d408095847dc0 100644 (file)
@@ -1828,8 +1828,13 @@ class _ProtocolMeta(ABCMeta):
                 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 (file)
index 0000000..b2b9894
--- /dev/null
@@ -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.