| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,6 @@ | |||
| 1 | 1 | import contextlib | |
| 2 | 2 | import collections | |
| 3 | + import collections.abc | ||
| 3 | 4 | from collections import defaultdict | |
| 4 | 5 | from functools import lru_cache, wraps | |
| 5 | 6 | import inspect | |
@@ -2722,19 +2723,41 @@ def x(self): ... | |||
| 2722 | 2723 | self.assertIsSubclass(C, PG) | |
| 2723 | 2724 | self.assertIsSubclass(BadP, PG) | |
| 2724 | 2725 | ||
| 2725 | - with self.assertRaises(TypeError): | ||
| 2726 | + no_subscripted_generics = ( | ||
| 2727 | + "Subscripted generics cannot be used with class and instance checks" | ||
| 2728 | + ) | ||
| 2729 | + | ||
| 2730 | + with self.assertRaisesRegex(TypeError, no_subscripted_generics): | ||
| 2726 | 2731 | issubclass(C, PG[T]) | |
| 2727 | - with self.assertRaises(TypeError): | ||
| 2732 | + with self.assertRaisesRegex(TypeError, no_subscripted_generics): | ||
| 2728 | 2733 | issubclass(C, PG[C]) | |
| 2729 | - with self.assertRaises(TypeError): | ||
| 2734 | + | ||
| 2735 | + only_runtime_checkable_protocols = ( | ||
| 2736 | + "Instance and class checks can only be used with " | ||
| 2737 | + "@runtime_checkable protocols" | ||
| 2738 | + ) | ||
| 2739 | + | ||
| 2740 | + with self.assertRaisesRegex(TypeError, only_runtime_checkable_protocols): | ||
| 2730 | 2741 | issubclass(C, BadP) | |
| 2731 | - with self.assertRaises(TypeError): | ||
| 2742 | + with self.assertRaisesRegex(TypeError, only_runtime_checkable_protocols): | ||
| 2732 | 2743 | issubclass(C, BadPG) | |
| 2733 | - with self.assertRaises(TypeError): | ||
| 2744 | + | ||
| 2745 | + with self.assertRaisesRegex(TypeError, no_subscripted_generics): | ||
| 2734 | 2746 | issubclass(P, PG[T]) | |
| 2735 | - with self.assertRaises(TypeError): | ||
| 2747 | + with self.assertRaisesRegex(TypeError, no_subscripted_generics): | ||
| 2736 | 2748 | issubclass(PG, PG[int]) | |
| 2737 | 2749 | ||
| 2750 | + only_classes_allowed = r"issubclass\(\) arg 1 must be a class" | ||
| 2751 | + | ||
| 2752 | + with self.assertRaisesRegex(TypeError, only_classes_allowed): | ||
| 2753 | + issubclass(1, P) | ||
| 2754 | + with self.assertRaisesRegex(TypeError, only_classes_allowed): | ||
| 2755 | + issubclass(1, PG) | ||
| 2756 | + with self.assertRaisesRegex(TypeError, only_classes_allowed): | ||
| 2757 | + issubclass(1, BadP) | ||
| 2758 | + with self.assertRaisesRegex(TypeError, only_classes_allowed): | ||
| 2759 | + issubclass(1, BadPG) | ||
| 2760 | + | ||
| 2738 | 2761 | def test_protocols_issubclass_non_callable(self): | |
| 2739 | 2762 | class C: | |
| 2740 | 2763 | x = 1 | |
@@ -2743,12 +2766,19 @@ class C: | |||
| 2743 | 2766 | class PNonCall(Protocol): | |
| 2744 | 2767 | x = 1 | |
| 2745 | 2768 | ||
| 2746 | - with self.assertRaises(TypeError): | ||
| 2769 | + non_callable_members_illegal = ( | ||
| 2770 | + "Protocols with non-method members don't support issubclass()" | ||
| 2771 | + ) | ||
| 2772 | + | ||
| 2773 | + with self.assertRaisesRegex(TypeError, non_callable_members_illegal): | ||
| 2747 | 2774 | issubclass(C, PNonCall) | |
| 2775 | + | ||
| 2748 | 2776 | self.assertIsInstance(C(), PNonCall) | |
| 2749 | 2777 | PNonCall.register(C) | |
| 2750 | - with self.assertRaises(TypeError): | ||
| 2778 | + | ||
| 2779 | + with self.assertRaisesRegex(TypeError, non_callable_members_illegal): | ||
| 2751 | 2780 | issubclass(C, PNonCall) | |
| 2781 | + | ||
| 2752 | 2782 | self.assertIsInstance(C(), PNonCall) | |
| 2753 | 2783 | ||
| 2754 | 2784 | # check that non-protocol subclasses are not affected | |
@@ -2759,7 +2789,8 @@ class D(PNonCall): ... | |||
| 2759 | 2789 | D.register(C) | |
| 2760 | 2790 | self.assertIsSubclass(C, D) | |
| 2761 | 2791 | self.assertIsInstance(C(), D) | |
| 2762 | - with self.assertRaises(TypeError): | ||
| 2792 | + | ||
| 2793 | + with self.assertRaisesRegex(TypeError, non_callable_members_illegal): | ||
| 2763 | 2794 | issubclass(D, PNonCall) | |
| 2764 | 2795 | ||
| 2765 | 2796 | def test_no_weird_caching_with_issubclass_after_isinstance(self): | |
@@ -2778,7 +2809,10 @@ def __init__(self) -> None: | |||
| 2778 | 2809 | # as the cached result of the isinstance() check immediately above | |
| 2779 | 2810 | # would mean the issubclass() call would short-circuit | |
| 2780 | 2811 | # before we got to the "raise TypeError" line | |
| 2781 | - with self.assertRaises(TypeError): | ||
| 2812 | + with self.assertRaisesRegex( | ||
| 2813 | + TypeError, | ||
| 2814 | + "Protocols with non-method members don't support issubclass()" | ||
| 2815 | + ): | ||
| 2782 | 2816 | issubclass(Eggs, Spam) | |
| 2783 | 2817 | ||
| 2784 | 2818 | def test_no_weird_caching_with_issubclass_after_isinstance_2(self): | |
@@ -2795,7 +2829,10 @@ class Eggs: ... | |||
| 2795 | 2829 | # as the cached result of the isinstance() check immediately above | |
| 2796 | 2830 | # would mean the issubclass() call would short-circuit | |
| 2797 | 2831 | # before we got to the "raise TypeError" line | |
| 2798 | - with self.assertRaises(TypeError): | ||
| 2832 | + with self.assertRaisesRegex( | ||
| 2833 | + TypeError, | ||
| 2834 | + "Protocols with non-method members don't support issubclass()" | ||
| 2835 | + ): | ||
| 2799 | 2836 | issubclass(Eggs, Spam) | |
| 2800 | 2837 | ||
| 2801 | 2838 | def test_no_weird_caching_with_issubclass_after_isinstance_3(self): | |
@@ -2816,7 +2853,10 @@ def __getattr__(self, attr): | |||
| 2816 | 2853 | # as the cached result of the isinstance() check immediately above | |
| 2817 | 2854 | # would mean the issubclass() call would short-circuit | |
| 2818 | 2855 | # before we got to the "raise TypeError" line | |
| 2819 | - with self.assertRaises(TypeError): | ||
| 2856 | + with self.assertRaisesRegex( | ||
| 2857 | + TypeError, | ||
| 2858 | + "Protocols with non-method members don't support issubclass()" | ||
| 2859 | + ): | ||
| 2820 | 2860 | issubclass(Eggs, Spam) | |
| 2821 | 2861 | ||
| 2822 | 2862 | def test_no_weird_caching_with_issubclass_after_isinstance_pep695(self): | |
@@ -2835,7 +2875,10 @@ def __init__(self, x: T) -> None: | |||
| 2835 | 2875 | # as the cached result of the isinstance() check immediately above | |
| 2836 | 2876 | # would mean the issubclass() call would short-circuit | |
| 2837 | 2877 | # before we got to the "raise TypeError" line | |
| 2838 | - with self.assertRaises(TypeError): | ||
| 2878 | + with self.assertRaisesRegex( | ||
| 2879 | + TypeError, | ||
| 2880 | + "Protocols with non-method members don't support issubclass()" | ||
| 2881 | + ): | ||
| 2839 | 2882 | issubclass(Eggs, Spam) | |
| 2840 | 2883 | ||
| 2841 | 2884 | def test_protocols_isinstance(self): | |
@@ -2883,13 +2926,21 @@ def __init__(self): | |||
| 2883 | 2926 | with self.subTest(klass=klass.__name__, proto=proto.__name__): | |
| 2884 | 2927 | self.assertIsInstance(klass(), proto) | |
| 2885 | 2928 | ||
| 2886 | - with self.assertRaises(TypeError): | ||
| 2929 | + no_subscripted_generics = "Subscripted generics cannot be used with class and instance checks" | ||
| 2930 | + | ||
| 2931 | + with self.assertRaisesRegex(TypeError, no_subscripted_generics): | ||
| 2887 | 2932 | isinstance(C(), PG[T]) | |
| 2888 | - with self.assertRaises(TypeError): | ||
| 2933 | + with self.assertRaisesRegex(TypeError, no_subscripted_generics): | ||
| 2889 | 2934 | isinstance(C(), PG[C]) | |
| 2890 | - with self.assertRaises(TypeError): | ||
| 2935 | + | ||
| 2936 | + only_runtime_checkable_msg = ( | ||
| 2937 | + "Instance and class checks can only be used " | ||
| 2938 | + "with @runtime_checkable protocols" | ||
| 2939 | + ) | ||
| 2940 | + | ||
| 2941 | + with self.assertRaisesRegex(TypeError, only_runtime_checkable_msg): | ||
| 2891 | 2942 | isinstance(C(), BadP) | |
| 2892 | - with self.assertRaises(TypeError): | ||
| 2943 | + with self.assertRaisesRegex(TypeError, only_runtime_checkable_msg): | ||
| 2893 | 2944 | isinstance(C(), BadPG) | |
| 2894 | 2945 | ||
| 2895 | 2946 | def test_protocols_isinstance_properties_and_descriptors(self): | |
@@ -3274,7 +3325,7 @@ class P(Protocol): | |||
| 3274 | 3325 | ||
| 3275 | 3326 | class C: pass | |
| 3276 | 3327 | ||
| 3277 | - with self.assertRaises(TypeError): | ||
| 3328 | + with self.assertRaisesRegex(TypeError, r"issubclass\(\) arg 1 must be a class"): | ||
| 3278 | 3329 | issubclass(C(), P) | |
| 3279 | 3330 | ||
| 3280 | 3331 | def test_defining_generic_protocols(self): | |
@@ -3654,6 +3705,28 @@ def __init__(self): | |||
| 3654 | 3705 | ||
| 3655 | 3706 | Foo() # Previously triggered RecursionError | |
| 3656 | 3707 | ||
| 3708 | + def test_interaction_with_isinstance_checks_on_superclasses_with_ABCMeta(self): | ||
| 3709 | + # Ensure the cache is empty, or this test won't work correctly | ||
| 3710 | + collections.abc.Sized._abc_registry_clear() | ||
| 3711 | + | ||
| 3712 | + class Foo(collections.abc.Sized, Protocol): pass | ||
| 3713 | + | ||
| 3714 | + # gh-105144: this previously raised TypeError | ||
| 3715 | + # if a Protocol subclass of Sized had been created | ||
| 3716 | + # before any isinstance() checks against Sized | ||
| 3717 | + self.assertNotIsInstance(1, collections.abc.Sized) | ||
| 3718 | + | ||
| 3719 | + def test_interaction_with_isinstance_checks_on_superclasses_with_ABCMeta_2(self): | ||
| 3720 | + # Ensure the cache is empty, or this test won't work correctly | ||
| 3721 | + collections.abc.Sized._abc_registry_clear() | ||
| 3722 | + | ||
| 3723 | + class Foo(typing.Sized, Protocol): pass | ||
| 3724 | + | ||
| 3725 | + # gh-105144: this previously raised TypeError | ||
| 3726 | + # if a Protocol subclass of Sized had been created | ||
| 3727 | + # before any isinstance() checks against Sized | ||
| 3728 | + self.assertNotIsInstance(1, typing.Sized) | ||
| 3729 | + | ||
| 3657 | 3730 | ||
| 3658 | 3731 | class GenericTests(BaseTestCase): | |
| 3659 | 3732 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1733,7 +1733,7 @@ def _caller(depth=1, default='__main__'): | |||
| 1733 | 1733 | pass | |
| 1734 | 1734 | return None | |
| 1735 | 1735 | ||
| 1736 | - def _allow_reckless_class_checks(depth=3): | ||
| 1736 | + def _allow_reckless_class_checks(depth=2): | ||
| 1737 | 1737 | """Allow instance and class checks for special stdlib modules. | |
| 1738 | 1738 | ||
| 1739 | 1739 | The abc and functools modules indiscriminately call isinstance() and | |
@@ -1788,14 +1788,22 @@ def __init__(cls, *args, **kwargs): | |||
| 1788 | 1788 | ) | |
| 1789 | 1789 | ||
| 1790 | 1790 | def __subclasscheck__(cls, other): | |
| 1791 | + if not isinstance(other, type): | ||
| 1792 | + # Same error message as for issubclass(1, int). | ||
| 1793 | + raise TypeError('issubclass() arg 1 must be a class') | ||
| 1791 | 1794 | if ( | |
| 1792 | 1795 | getattr(cls, '_is_protocol', False) | |
| 1793 | - and not cls.__callable_proto_members_only__ | ||
| 1794 | - and not _allow_reckless_class_checks(depth=2) | ||
| 1796 | + and not _allow_reckless_class_checks() | ||
| 1795 | 1797 | ): | |
| 1796 | - raise TypeError( | ||
| 1797 | - "Protocols with non-method members don't support issubclass()" | ||
| 1798 | - ) | ||
| 1798 | + if not cls.__callable_proto_members_only__: | ||
| 1799 | + raise TypeError( | ||
| 1800 | + "Protocols with non-method members don't support issubclass()" | ||
| 1801 | + ) | ||
| 1802 | + if not getattr(cls, '_is_runtime_protocol', False): | ||
| 1803 | + raise TypeError( | ||
| 1804 | + "Instance and class checks can only be used with " | ||
| 1805 | + "@runtime_checkable protocols" | ||
| 1806 | + ) | ||
| 1799 | 1807 | return super().__subclasscheck__(other) | |
| 1800 | 1808 | ||
| 1801 | 1809 | def __instancecheck__(cls, instance): | |
@@ -1807,7 +1815,7 @@ def __instancecheck__(cls, instance): | |||
| 1807 | 1815 | ||
| 1808 | 1816 | if ( | |
| 1809 | 1817 | not getattr(cls, '_is_runtime_protocol', False) and | |
| 1810 | - not _allow_reckless_class_checks(depth=2) | ||
| 1818 | + not _allow_reckless_class_checks() | ||
| 1811 | 1819 | ): | |
| 1812 | 1820 | raise TypeError("Instance and class checks can only be used with" | |
| 1813 | 1821 | " @runtime_checkable protocols") | |
@@ -1875,18 +1883,6 @@ def _proto_hook(other): | |||
| 1875 | 1883 | if not cls.__dict__.get('_is_protocol', False): | |
| 1876 | 1884 | return NotImplemented | |
| 1877 | 1885 | ||
| 1878 | - # First, perform various sanity checks. | ||
| 1879 | - if not getattr(cls, '_is_runtime_protocol', False): | ||
| 1880 | - if _allow_reckless_class_checks(): | ||
| 1881 | - return NotImplemented | ||
| 1882 | - raise TypeError("Instance and class checks can only be used with" | ||
| 1883 | - " @runtime_checkable protocols") | ||
| 1884 | - | ||
| 1885 | - if not isinstance(other, type): | ||
| 1886 | - # Same error message as for issubclass(1, int). | ||
| 1887 | - raise TypeError('issubclass() arg 1 must be a class') | ||
| 1888 | - | ||
| 1889 | - # Second, perform the actual structural compatibility check. | ||
| 1890 | 1886 | for attr in cls.__protocol_attrs__: | |
| 1891 | 1887 | for base in other.__mro__: | |
| 1892 | 1888 | # Check if the members appears in the class dictionary... | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + Fix a recent regression in the :mod:`typing` module. The regression meant | ||
| 2 | + that doing ``class Foo(X, typing.Protocol)``, where ``X`` was a class that | ||
| 3 | + had :class:`abc.ABCMeta` as its metaclass, would then cause subsequent | ||
| 4 | + ``isinstance(1, X)`` calls to erroneously raise :exc:`TypeError`. Patch by | ||
| 5 | + Alex Waygood. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments