summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-03-11 01:45:40 (GMT)
committerGitHub <noreply@github.com>2023-03-11 01:45:40 (GMT)
commit281078794f10a42d0aa99d660e25a434fde96ec4 (patch)
treebb44e0b45af2aa02084789b51107af02afa7eff8
parent7276ee0d122d072486b4a9fc03d17533f871da8f (diff)
downloadcpython-281078794f10a42d0aa99d660e25a434fde96ec4.zip
cpython-281078794f10a42d0aa99d660e25a434fde96ec4.tar.gz
cpython-281078794f10a42d0aa99d660e25a434fde96ec4.tar.bz2
gh-102433: Add tests for how classes with properties interact with `isinstance()` checks on `typing.runtime_checkable` protocols (GH-102449)
(cherry picked from commit 5ffdaf748d98da6065158534720f1996a45a0072) Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Carl Meyer <carl@oddbird.net>
-rw-r--r--Lib/test/test_typing.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 4e0a4f5..a4d2434 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -1142,6 +1142,94 @@ class ProtocolTests(BaseTestCase):
with self.assertRaises(TypeError):
isinstance(C(), BadPG)
+ def test_protocols_isinstance_properties_and_descriptors(self):
+ class C:
+ @property
+ def attr(self):
+ return 42
+
+ class CustomDescriptor:
+ def __get__(self, obj, objtype=None):
+ return 42
+
+ class D:
+ attr = CustomDescriptor()
+
+ # Check that properties set on superclasses
+ # are still found by the isinstance() logic
+ class E(C): ...
+ class F(D): ...
+
+ class Empty: ...
+
+ T = TypeVar('T')
+
+ @runtime_checkable
+ class P(Protocol):
+ @property
+ def attr(self): ...
+
+ @runtime_checkable
+ class P1(Protocol):
+ attr: int
+
+ @runtime_checkable
+ class PG(Protocol[T]):
+ @property
+ def attr(self): ...
+
+ @runtime_checkable
+ class PG1(Protocol[T]):
+ attr: T
+
+ for protocol_class in P, P1, PG, PG1:
+ for klass in C, D, E, F:
+ with self.subTest(
+ klass=klass.__name__,
+ protocol_class=protocol_class.__name__
+ ):
+ self.assertIsInstance(klass(), protocol_class)
+
+ with self.subTest(klass="Empty", protocol_class=protocol_class.__name__):
+ self.assertNotIsInstance(Empty(), protocol_class)
+
+ class BadP(Protocol):
+ @property
+ def attr(self): ...
+
+ class BadP1(Protocol):
+ attr: int
+
+ class BadPG(Protocol[T]):
+ @property
+ def attr(self): ...
+
+ class BadPG1(Protocol[T]):
+ attr: T
+
+ for obj in PG[T], PG[C], PG1[T], PG1[C], BadP, BadP1, BadPG, BadPG1:
+ for klass in C, D, E, F, Empty:
+ with self.subTest(klass=klass.__name__, obj=obj):
+ with self.assertRaises(TypeError):
+ isinstance(klass(), obj)
+
+ def test_protocols_isinstance_not_fooled_by_custom_dir(self):
+ @runtime_checkable
+ class HasX(Protocol):
+ x: int
+
+ class CustomDirWithX:
+ x = 10
+ def __dir__(self):
+ return []
+
+ class CustomDirWithoutX:
+ def __dir__(self):
+ return ["x"]
+
+ self.assertIsInstance(CustomDirWithX(), HasX)
+ self.assertNotIsInstance(CustomDirWithoutX(), HasX)
+
def test_protocols_isinstance_py36(self):
class APoint:
def __init__(self, x, y, label):