diff options
author | Alex Waygood <Alex.Waygood@Gmail.com> | 2023-04-02 13:22:19 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-02 13:22:19 (GMT) |
commit | 6d59c9e32ebb6d8468c7eb26e7cf3efd458c7a73 (patch) | |
tree | 908d594aba573e0e8d2e2b7edb87a8b4a93101df /Lib/test/test_typing.py | |
parent | d828b35785eeb590b8ca92684038f33177989e46 (diff) | |
download | cpython-6d59c9e32ebb6d8468c7eb26e7cf3efd458c7a73.zip cpython-6d59c9e32ebb6d8468c7eb26e7cf3efd458c7a73.tar.gz cpython-6d59c9e32ebb6d8468c7eb26e7cf3efd458c7a73.tar.bz2 |
gh-102433: Use `inspect.getattr_static` in `typing._ProtocolMeta.__instancecheck__` (#103034)
Diffstat (limited to 'Lib/test/test_typing.py')
-rw-r--r-- | Lib/test/test_typing.py | 93 |
1 files changed, 91 insertions, 2 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 23bf2b5..7d2e6a6 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2637,7 +2637,15 @@ class ProtocolTests(BaseTestCase): class PG1(Protocol[T]): attr: T - for protocol_class in P, P1, PG, PG1: + @runtime_checkable + class MethodP(Protocol): + def attr(self): ... + + @runtime_checkable + class MethodPG(Protocol[T]): + def attr(self) -> T: ... + + for protocol_class in P, P1, PG, PG1, MethodP, MethodPG: for klass in C, D, E, F: with self.subTest( klass=klass.__name__, @@ -2662,7 +2670,12 @@ class ProtocolTests(BaseTestCase): class BadPG1(Protocol[T]): attr: T - for obj in PG[T], PG[C], PG1[T], PG1[C], BadP, BadP1, BadPG, BadPG1: + cases = ( + PG[T], PG[C], PG1[T], PG1[C], MethodPG[T], + MethodPG[C], BadP, BadP1, BadPG, BadPG1 + ) + + for obj in cases: for klass in C, D, E, F, Empty: with self.subTest(klass=klass.__name__, obj=obj): with self.assertRaises(TypeError): @@ -2685,6 +2698,82 @@ class ProtocolTests(BaseTestCase): self.assertIsInstance(CustomDirWithX(), HasX) self.assertNotIsInstance(CustomDirWithoutX(), HasX) + def test_protocols_isinstance_attribute_access_with_side_effects(self): + class C: + @property + def attr(self): + raise AttributeError('no') + + class CustomDescriptor: + def __get__(self, obj, objtype=None): + raise RuntimeError("NO") + + class D: + attr = CustomDescriptor() + + # Check that properties set on superclasses + # are still found by the isinstance() logic + class E(C): ... + class F(D): ... + + class WhyWouldYouDoThis: + def __getattr__(self, name): + raise RuntimeError("wut") + + 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 + + @runtime_checkable + class MethodP(Protocol): + def attr(self): ... + + @runtime_checkable + class MethodPG(Protocol[T]): + def attr(self) -> T: ... + + for protocol_class in P, P1, PG, PG1, MethodP, MethodPG: + 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="WhyWouldYouDoThis", + protocol_class=protocol_class.__name__ + ): + self.assertNotIsInstance(WhyWouldYouDoThis(), protocol_class) + + def test_protocols_isinstance___slots__(self): + # As per the consensus in https://github.com/python/typing/issues/1367, + # this is desirable behaviour + @runtime_checkable + class HasX(Protocol): + x: int + + class HasNothingButSlots: + __slots__ = ("x",) + + self.assertIsInstance(HasNothingButSlots(), HasX) + def test_protocols_isinstance_py36(self): class APoint: def __init__(self, x, y, label): |