|
24 | 24 | from typing import Generic, ClassVar, Final, final, Protocol
|
25 | 25 | from typing import assert_type, cast, runtime_checkable
|
26 | 26 | from typing import get_type_hints
|
27 |
| -from typing import get_origin, get_args |
| 27 | +from typing import get_origin, get_args, get_protocol_members |
28 | 28 | from typing import override
|
29 |
| -from typing import is_typeddict |
| 29 | +from typing import is_typeddict, is_protocol |
30 | 30 | from typing import reveal_type
|
31 | 31 | from typing import dataclass_transform
|
32 | 32 | from typing import no_type_check, no_type_check_decorator
|
@@ -3363,6 +3363,18 @@ def meth(self): pass
|
3363 | 3363 | self.assertNotIn("__callable_proto_members_only__", vars(NonP))
|
3364 | 3364 | self.assertNotIn("__callable_proto_members_only__", vars(NonPR))
|
3365 | 3365 |
|
| 3366 | + self.assertEqual(get_protocol_members(P), {"x"}) |
| 3367 | + self.assertEqual(get_protocol_members(PR), {"meth"}) |
| 3368 | + |
| 3369 | + # the returned object should be immutable, |
| 3370 | + # and should be a different object to the original attribute |
| 3371 | + # to prevent users from (accidentally or deliberately) |
| 3372 | + # mutating the attribute on the original class |
| 3373 | + self.assertIsInstance(get_protocol_members(P), frozenset) |
| 3374 | + self.assertIsNot(get_protocol_members(P), P.__protocol_attrs__) |
| 3375 | + self.assertIsInstance(get_protocol_members(PR), frozenset) |
| 3376 | + self.assertIsNot(get_protocol_members(PR), P.__protocol_attrs__) |
| 3377 | + |
3366 | 3378 | acceptable_extra_attrs = {
|
3367 | 3379 | '_is_protocol', '_is_runtime_protocol', '__parameters__',
|
3368 | 3380 | '__init__', '__annotations__', '__subclasshook__',
|
@@ -3778,6 +3790,59 @@ def __init__(self):
|
3778 | 3790 |
|
3779 | 3791 | Foo() # Previously triggered RecursionError
|
3780 | 3792 |
|
| 3793 | + def test_get_protocol_members(self): |
| 3794 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3795 | + get_protocol_members(object) |
| 3796 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3797 | + get_protocol_members(object()) |
| 3798 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3799 | + get_protocol_members(Protocol) |
| 3800 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3801 | + get_protocol_members(Generic) |
| 3802 | + |
| 3803 | + class P(Protocol): |
| 3804 | + a: int |
| 3805 | + def b(self) -> str: ... |
| 3806 | + @property |
| 3807 | + def c(self) -> int: ... |
| 3808 | + |
| 3809 | + self.assertEqual(get_protocol_members(P), {'a', 'b', 'c'}) |
| 3810 | + self.assertIsInstance(get_protocol_members(P), frozenset) |
| 3811 | + self.assertIsNot(get_protocol_members(P), P.__protocol_attrs__) |
| 3812 | + |
| 3813 | + class Concrete: |
| 3814 | + a: int |
| 3815 | + def b(self) -> str: return "capybara" |
| 3816 | + @property |
| 3817 | + def c(self) -> int: return 5 |
| 3818 | + |
| 3819 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3820 | + get_protocol_members(Concrete) |
| 3821 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3822 | + get_protocol_members(Concrete()) |
| 3823 | + |
| 3824 | + class ConcreteInherit(P): |
| 3825 | + a: int = 42 |
| 3826 | + def b(self) -> str: return "capybara" |
| 3827 | + @property |
| 3828 | + def c(self) -> int: return 5 |
| 3829 | + |
| 3830 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3831 | + get_protocol_members(ConcreteInherit) |
| 3832 | + with self.assertRaisesRegex(TypeError, "not a Protocol"): |
| 3833 | + get_protocol_members(ConcreteInherit()) |
| 3834 | + |
| 3835 | + def test_is_protocol(self): |
| 3836 | + self.assertTrue(is_protocol(Proto)) |
| 3837 | + self.assertTrue(is_protocol(Point)) |
| 3838 | + self.assertFalse(is_protocol(Concrete)) |
| 3839 | + self.assertFalse(is_protocol(Concrete())) |
| 3840 | + self.assertFalse(is_protocol(Generic)) |
| 3841 | + self.assertFalse(is_protocol(object)) |
| 3842 | + |
| 3843 | + # Protocol is not itself a protocol |
| 3844 | + self.assertFalse(is_protocol(Protocol)) |
| 3845 | + |
3781 | 3846 | def test_interaction_with_isinstance_checks_on_superclasses_with_ABCMeta(self):
|
3782 | 3847 | # Ensure the cache is empty, or this test won't work correctly
|
3783 | 3848 | collections.abc.Sized._abc_registry_clear()
|
|
0 commit comments