diff options
author | Randolf Scholz <randolf.scholz@gmail.com> | 2023-11-24 09:46:08 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-24 09:46:08 (GMT) |
commit | e9d1360c9a1072aa23950b321491dc542c3a19b8 (patch) | |
tree | d1475279265198f6cc44e4e53fb6be2b0e9a1a1b | |
parent | d9fc15222e96942e30ea8b0561dec5c82ecb4663 (diff) | |
download | cpython-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.py | 16 | ||||
-rw-r--r-- | Lib/typing.py | 7 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst | 3 |
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. |