summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRandolf Scholz <randolf.scholz@gmail.com>2023-11-24 09:46:08 (GMT)
committerGitHub <noreply@github.com>2023-11-24 09:46:08 (GMT)
commite9d1360c9a1072aa23950b321491dc542c3a19b8 (patch)
treed1475279265198f6cc44e4e53fb6be2b0e9a1a1b
parentd9fc15222e96942e30ea8b0561dec5c82ecb4663 (diff)
downloadcpython-e9d1360c9a1072aa23950b321491dc542c3a19b8.zip
cpython-e9d1360c9a1072aa23950b321491dc542c3a19b8.tar.gz
cpython-e9d1360c9a1072aa23950b321491dc542c3a19b8.tar.bz2
gh-112345: `typing.Protocol`: Let failed subclasscheck show non-method members (#112344)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
-rw-r--r--Lib/test/test_typing.py16
-rw-r--r--Lib/typing.py7
-rw-r--r--Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst3
3 files changed, 25 insertions, 1 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 2b5f34b..31d7fda 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -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):
diff --git a/Lib/typing.py b/Lib/typing.py
index a96c708..872aca8 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -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
index 0000000..b2b9894
--- /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.