summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-06-05 07:06:25 -0700
committerGitHub <noreply@github.com>2023-06-05 14:06:25 +0000
commit51750269cf36901e83a70a1fff9ab93a2e2855fb (patch)
tree4dcf9af09ca735e5c4db93c3f968f686ae8084f7
parent[3.12] gh-105164: Detect annotations inside match blocks (GH-105177) (#105313) (diff)
downloadcpython-51750269cf36901e83a70a1fff9ab93a2e2855fb.tar.gz
cpython-51750269cf36901e83a70a1fff9ab93a2e2855fb.tar.bz2
cpython-51750269cf36901e83a70a1fff9ab93a2e2855fb.zip
[3.12] gh-105237: Allow calling `issubclass(X, typing.Protocol)` again (GH-105239) (#105316)
gh-105237: Allow calling `issubclass(X, typing.Protocol)` again (GH-105239) (cherry picked from commit cdfb201bfa35b7c50de5099c6d9078c806851d98) Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
-rw-r--r--Lib/test/test_typing.py59
-rw-r--r--Lib/typing.py4
-rw-r--r--Misc/NEWS.d/next/Library/2023-06-02-14-57-11.gh-issue-105239.SAmuuj.rst2
3 files changed, 65 insertions, 0 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 5480a981ad5..3a2f7393eb4 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -2758,6 +2758,65 @@ class ProtocolTests(BaseTestCase):
with self.assertRaisesRegex(TypeError, only_classes_allowed):
issubclass(1, BadPG)
+ def test_issubclass_and_isinstance_on_Protocol_itself(self):
+ class C:
+ def x(self): pass
+
+ self.assertNotIsSubclass(object, Protocol)
+ self.assertNotIsInstance(object(), Protocol)
+
+ self.assertNotIsSubclass(str, Protocol)
+ self.assertNotIsInstance('foo', Protocol)
+
+ self.assertNotIsSubclass(C, Protocol)
+ self.assertNotIsInstance(C(), Protocol)
+
+ only_classes_allowed = r"issubclass\(\) arg 1 must be a class"
+
+ with self.assertRaisesRegex(TypeError, only_classes_allowed):
+ issubclass(1, Protocol)
+ with self.assertRaisesRegex(TypeError, only_classes_allowed):
+ issubclass('foo', Protocol)
+ with self.assertRaisesRegex(TypeError, only_classes_allowed):
+ issubclass(C(), Protocol)
+
+ T = TypeVar('T')
+
+ @runtime_checkable
+ class EmptyProtocol(Protocol): pass
+
+ @runtime_checkable
+ class SupportsStartsWith(Protocol):
+ def startswith(self, x: str) -> bool: ...
+
+ @runtime_checkable
+ class SupportsX(Protocol[T]):
+ def x(self): ...
+
+ for proto in EmptyProtocol, SupportsStartsWith, SupportsX:
+ with self.subTest(proto=proto.__name__):
+ self.assertIsSubclass(proto, Protocol)
+
+ # gh-105237 / PR #105239:
+ # check that the presence of Protocol subclasses
+ # where `issubclass(X, <subclass>)` evaluates to True
+ # doesn't influence the result of `issubclass(X, Protocol)`
+
+ self.assertIsSubclass(object, EmptyProtocol)
+ self.assertIsInstance(object(), EmptyProtocol)
+ self.assertNotIsSubclass(object, Protocol)
+ self.assertNotIsInstance(object(), Protocol)
+
+ self.assertIsSubclass(str, SupportsStartsWith)
+ self.assertIsInstance('foo', SupportsStartsWith)
+ self.assertNotIsSubclass(str, Protocol)
+ self.assertNotIsInstance('foo', Protocol)
+
+ self.assertIsSubclass(C, SupportsX)
+ self.assertIsInstance(C(), SupportsX)
+ self.assertNotIsSubclass(C, Protocol)
+ self.assertNotIsInstance(C(), Protocol)
+
def test_protocols_issubclass_non_callable(self):
class C:
x = 1
diff --git a/Lib/typing.py b/Lib/typing.py
index 2383d807ec5..06d50306c47 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -1788,6 +1788,8 @@ class _ProtocolMeta(ABCMeta):
)
def __subclasscheck__(cls, other):
+ if cls is Protocol:
+ return type.__subclasscheck__(cls, other)
if not isinstance(other, type):
# Same error message as for issubclass(1, int).
raise TypeError('issubclass() arg 1 must be a class')
@@ -1809,6 +1811,8 @@ class _ProtocolMeta(ABCMeta):
def __instancecheck__(cls, instance):
# We need this method for situations where attributes are
# assigned in __init__.
+ if cls is Protocol:
+ return type.__instancecheck__(cls, instance)
if not getattr(cls, "_is_protocol", False):
# i.e., it's a concrete subclass of a protocol
return super().__instancecheck__(instance)
diff --git a/Misc/NEWS.d/next/Library/2023-06-02-14-57-11.gh-issue-105239.SAmuuj.rst b/Misc/NEWS.d/next/Library/2023-06-02-14-57-11.gh-issue-105239.SAmuuj.rst
new file mode 100644
index 00000000000..35e1b1a217b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-06-02-14-57-11.gh-issue-105239.SAmuuj.rst
@@ -0,0 +1,2 @@
+Fix longstanding bug where ``issubclass(object, typing.Protocol)`` would
+evaluate to ``True`` in some edge cases. Patch by Alex Waygood.