]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-103171: Revert undocumented behaviour change for runtime-checkable protocol...
authorAlex Waygood <Alex.Waygood@Gmail.com>
Wed, 7 Jun 2023 21:18:21 +0000 (22:18 +0100)
committerGitHub <noreply@github.com>
Wed, 7 Jun 2023 21:18:21 +0000 (21:18 +0000)
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2023-06-07-14-24-39.gh-issue-103171.b3VJMD.rst [new file with mode: 0644]

index 4dfe45302386f25a7262e8d921e79dc59e3b8b72..316b30076394a94d0a1199f30ca30e99ddaf7a9b 100644 (file)
@@ -3349,6 +3349,71 @@ class ProtocolTests(BaseTestCase):
 
         Foo()  # Previously triggered RecursionError
 
+    def test_empty_protocol_decorated_with_final(self):
+        @final
+        @runtime_checkable
+        class EmptyProtocol(Protocol): ...
+
+        self.assertIsSubclass(object, EmptyProtocol)
+        self.assertIsInstance(object(), EmptyProtocol)
+
+    def test_protocol_decorated_with_final_callable_members(self):
+        @final
+        @runtime_checkable
+        class ProtocolWithMethod(Protocol):
+            def startswith(self, string: str) -> bool: ...
+
+        self.assertIsSubclass(str, ProtocolWithMethod)
+        self.assertNotIsSubclass(int, ProtocolWithMethod)
+        self.assertIsInstance('foo', ProtocolWithMethod)
+        self.assertNotIsInstance(42, ProtocolWithMethod)
+
+    def test_protocol_decorated_with_final_noncallable_members(self):
+        @final
+        @runtime_checkable
+        class ProtocolWithNonCallableMember(Protocol):
+            x: int
+
+        class Foo:
+            x = 42
+
+        only_callable_members_please = (
+            r"Protocols with non-method members don't support issubclass()"
+        )
+
+        with self.assertRaisesRegex(TypeError, only_callable_members_please):
+            issubclass(Foo, ProtocolWithNonCallableMember)
+
+        with self.assertRaisesRegex(TypeError, only_callable_members_please):
+            issubclass(int, ProtocolWithNonCallableMember)
+
+        self.assertIsInstance(Foo(), ProtocolWithNonCallableMember)
+        self.assertNotIsInstance(42, ProtocolWithNonCallableMember)
+
+    def test_protocol_decorated_with_final_mixed_members(self):
+        @final
+        @runtime_checkable
+        class ProtocolWithMixedMembers(Protocol):
+            x: int
+            def method(self) -> None: ...
+
+        class Foo:
+            x = 42
+            def method(self) -> None: ...
+
+        only_callable_members_please = (
+            r"Protocols with non-method members don't support issubclass()"
+        )
+
+        with self.assertRaisesRegex(TypeError, only_callable_members_please):
+            issubclass(Foo, ProtocolWithMixedMembers)
+
+        with self.assertRaisesRegex(TypeError, only_callable_members_please):
+            issubclass(int, ProtocolWithMixedMembers)
+
+        self.assertIsInstance(Foo(), ProtocolWithMixedMembers)
+        self.assertNotIsInstance(42, ProtocolWithMixedMembers)
+
 
 class GenericTests(BaseTestCase):
 
index e6ef344ef43a2bfb353d74929e9b04454f696ed7..f01b8c38a8f19b4bad06220e1cc4ae97c7b27367 100644 (file)
@@ -1883,7 +1883,7 @@ class _TypingEllipsis:
 
 
 _TYPING_INTERNALS = ['__parameters__', '__orig_bases__',  '__orig_class__',
-                     '_is_protocol', '_is_runtime_protocol']
+                     '_is_protocol', '_is_runtime_protocol', '__final__']
 
 _SPECIAL_NAMES = ['__abstractmethods__', '__annotations__', '__dict__', '__doc__',
                   '__init__', '__module__', '__new__', '__slots__',
diff --git a/Misc/NEWS.d/next/Library/2023-06-07-14-24-39.gh-issue-103171.b3VJMD.rst b/Misc/NEWS.d/next/Library/2023-06-07-14-24-39.gh-issue-103171.b3VJMD.rst
new file mode 100644 (file)
index 0000000..8c424d4
--- /dev/null
@@ -0,0 +1,4 @@
+Revert undocumented behaviour change with runtime-checkable protocols
+decorated with :func:`typing.final` in Python 3.11. The behaviour change had
+meant that objects would not be considered instances of these protocols at
+runtime unless they had a ``__final__`` attribute. Patch by Alex Waygood.