From 317d18a574ee435b895eca2cc2d565ac6dc13f35 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 23 Nov 2023 17:48:47 +0100 Subject: [PATCH 01/11] Let failed protocol subclasscheck show non-method members --- Lib/typing.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index a96c7083eb785e..4affc1cadacf3d 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 = { + 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: {non_method_attrs}." ) if not getattr(cls, '_is_runtime_protocol', False): raise TypeError( From 100c309d3e3a3dc44074efd286af4fdb7bbd46ac Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:25:28 +0000 Subject: [PATCH 02/11] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst 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..511fe47fdc0c0b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst @@ -0,0 +1,2 @@ +Improve error message when trying to call :func:`issubclass` against a :class:`typing.Protocol` that has non-method members. +Patch by Randolf Scholz. From fd840b28acd7f64712e4c025bc1b2b5f07c58431 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 23 Nov 2023 18:56:04 +0100 Subject: [PATCH 03/11] added test --- Lib/test/test_typing.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2b5f34b4b92e0c..3c819516dff179 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4091,6 +4091,25 @@ 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 + + assert Vec2D.__protocol_attrs__ == {'x', 'y', 'square_norm'} + expected_error_message = ( + "Protocols with non-method members don't support issubclass()." + " Non-method members: {'x', 'y'}." + ) + + try: + issubclass(int, Vec2D) + except TypeError as exc: + self.assertEqual(str(exc), expected_error_message) + class GenericTests(BaseTestCase): From c70acc42deb0d2b027780cfb3628b18d5c6b6914 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 23 Nov 2023 19:01:12 +0100 Subject: [PATCH 04/11] Update Lib/test/test_typing.py Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 3c819516dff179..294fe08755ebde 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4099,7 +4099,7 @@ class Vec2D(Protocol): def square_norm(self) -> float: return self.x ** 2 + self.y ** 2 - assert Vec2D.__protocol_attrs__ == {'x', 'y', 'square_norm'} + 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'}." From 5db267099587224c0a4991ee96b79a69849192b6 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 23 Nov 2023 19:03:59 +0100 Subject: [PATCH 05/11] use assertRaisesRegex --- Lib/test/test_typing.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 294fe08755ebde..723c0754f2e7b9 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4099,17 +4099,12 @@ class Vec2D(Protocol): 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'}." ) - - try: - issubclass(int, Vec2D) - except TypeError as exc: - self.assertEqual(str(exc), expected_error_message) - + self.assertRaisesRegex(TypeError, f'^{re.escape(expected_error_message)}$') + self.assertEqual(Vec2D.__protocol_attrs__, {'x', 'y', 'square_norm'}) class GenericTests(BaseTestCase): From 71925d5323440d92bc167aba4270f5b7cf769ec1 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 23 Nov 2023 19:23:41 +0100 Subject: [PATCH 06/11] Update Lib/test/test_typing.py Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 723c0754f2e7b9..5d4a88d4082a90 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4103,7 +4103,9 @@ def square_norm(self) -> float: "Protocols with non-method members don't support issubclass()." " Non-method members: {'x', 'y'}." ) - self.assertRaisesRegex(TypeError, f'^{re.escape(expected_error_message)}$') + with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)): + issubclass(int, Vec2D) + self.assertEqual(Vec2D.__protocol_attrs__, {'x', 'y', 'square_norm'}) class GenericTests(BaseTestCase): From 998b45e6abc57d4b361a3b4d6cb8645dabd4b8fa Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 23 Nov 2023 19:26:57 +0100 Subject: [PATCH 07/11] sort members, fixed unit test --- Lib/test/test_typing.py | 4 ++-- Lib/typing.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 5d4a88d4082a90..372c621a376fb1 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4099,14 +4099,14 @@ class Vec2D(Protocol): 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'}." + " Non-method members: ['x', 'y']." ) with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)): issubclass(int, Vec2D) - self.assertEqual(Vec2D.__protocol_attrs__, {'x', 'y', 'square_norm'}) class GenericTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index 4affc1cadacf3d..0a11a61ef601f6 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1828,10 +1828,10 @@ def __subclasscheck__(cls, other): not cls.__callable_proto_members_only__ and cls.__dict__.get("__subclasshook__") is _proto_hook ): - non_method_attrs = { + 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()." f" Non-method members: {non_method_attrs}." From 77e3a4a68fc6fe3d46494c0cde9c50e0bda540b0 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 18:37:05 +0000 Subject: [PATCH 08/11] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst diff --git a/Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst b/Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst new file mode 100644 index 00000000000000..ecf99992d463e7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.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. From f2e3276b3d9b4108eaa4dbf852a670ed541afec9 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 23 Nov 2023 19:40:19 +0100 Subject: [PATCH 09/11] removed duplicate blurp --- .../Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst | 3 ++- .../Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst 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 index 511fe47fdc0c0b..b2b9894e6bef3a 100644 --- 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 @@ -1,2 +1,3 @@ -Improve error message when trying to call :func:`issubclass` against a :class:`typing.Protocol` that has non-method members. +Improve error message when trying to call :func:`issubclass` against a +:class:`typing.Protocol` that has non-method members. Patch by Randolf Scholz. diff --git a/Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst b/Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst deleted file mode 100644 index ecf99992d463e7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-23-18-37-04.gh-issue-112345.YJUAaE.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve error message when trying to call :func:`issubclass` against a -:class:`typing.Protocol` that has non-method members. -Patch by Randolf Scholz. From bc40f8433394e186d8fc61888f843d75286aed9e Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Fri, 24 Nov 2023 10:27:10 +0100 Subject: [PATCH 10/11] Update Lib/typing.py Co-authored-by: Alex Waygood --- Lib/typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index 0a11a61ef601f6..872aca82c4e779 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1834,7 +1834,7 @@ def __subclasscheck__(cls, other): ) raise TypeError( "Protocols with non-method members don't support issubclass()." - f" Non-method members: {non_method_attrs}." + f" Non-method members: {str(non_method_attrs)[1:-1]}." ) if not getattr(cls, '_is_runtime_protocol', False): raise TypeError( From 62b6f71c79d0615b9b824090f406e55927a030b9 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Fri, 24 Nov 2023 10:27:16 +0100 Subject: [PATCH 11/11] Update Lib/test/test_typing.py Co-authored-by: Alex Waygood --- Lib/test/test_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 372c621a376fb1..31d7fda2f978da 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4102,7 +4102,7 @@ def square_norm(self) -> float: 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']." + " Non-method members: 'x', 'y'." ) with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)): issubclass(int, Vec2D)