From c17b8edd713ce90e16cbc896373b118009e50ce1 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sun, 4 Jun 2023 12:17:23 +0100 Subject: [PATCH 1/4] Ensure `isinstance([], collections.abc.Mapping)` always evaluates to `False` --- Lib/test/test_typing.py | 14 ++++++++++ Lib/typing.py | 28 +++++++++++++------ ...-06-04-12-16-47.gh-issue-105280.srRbCe.rst | 4 +++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-06-04-12-16-47.gh-issue-105280.srRbCe.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index f7114eb1fdbdd9..c4d7c0be92f815 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3,6 +3,7 @@ import collections.abc from collections import defaultdict from functools import lru_cache, wraps +import gc import inspect import itertools import pickle @@ -2758,6 +2759,19 @@ def x(self): ... with self.assertRaisesRegex(TypeError, only_classes_allowed): issubclass(1, BadPG) + def test_isinstance_checks_not_at_whim_of_gc(self): + gc.disable() + self.addCleanup(gc.enable) + + with self.assertRaisesRegex( + TypeError, + "Protocols can only inherit from other protocols" + ): + class Foo(collections.abc.Mapping, Protocol): + pass + + self.assertNotIsInstance([], collections.abc.Mapping) + def test_protocols_issubclass_non_callable(self): class C: x = 1 diff --git a/Lib/typing.py b/Lib/typing.py index f589be7295c755..884c2230e93b76 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1771,6 +1771,25 @@ def _pickle_pskwargs(pskwargs): class _ProtocolMeta(ABCMeta): # This metaclass is somewhat unfortunate, # but is necessary for several reasons... + def __new__(mcls, name, bases, namespace, /, **kwargs): + if name == "Protocol" and bases == (Generic,): + pass + elif Protocol in bases: + for base in bases: + if not ( + base in {object, Generic} + or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, {}) + or ( + isinstance(base, _ProtocolMeta) + and getattr(base, "_is_protocol", False) + ) + ): + raise TypeError( + f"Protocols can only inherit from other protocols, " + f"got {base!r}" + ) + return super().__new__(mcls, name, bases, namespace, **kwargs) + def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) if getattr(cls, "_is_protocol", False): @@ -1902,14 +1921,7 @@ def _proto_hook(other): if not cls._is_protocol: return - # ... otherwise check consistency of bases, and prohibit instantiation. - for base in cls.__bases__: - if not (base in (object, Generic) or - base.__module__ in _PROTO_ALLOWLIST and - base.__name__ in _PROTO_ALLOWLIST[base.__module__] or - issubclass(base, Generic) and getattr(base, '_is_protocol', False)): - raise TypeError('Protocols can only inherit from other' - ' protocols, got %r' % base) + # ... otherwise prohibit instantiation. if cls.__init__ is Protocol.__init__: cls.__init__ = _no_init_or_replace_init diff --git a/Misc/NEWS.d/next/Library/2023-06-04-12-16-47.gh-issue-105280.srRbCe.rst b/Misc/NEWS.d/next/Library/2023-06-04-12-16-47.gh-issue-105280.srRbCe.rst new file mode 100644 index 00000000000000..8e469646604316 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-04-12-16-47.gh-issue-105280.srRbCe.rst @@ -0,0 +1,4 @@ +Fix bug where ``isinstance([], collections.abc.Mapping)`` could evaluate to +``True`` if garbage collection happened at the wrong time. The bug was +caused by changes to the implementation of :class:`typing.Protocol` in +Python 3.12. From 612c551870906e0f2a391950e4e7eb33fa0f4601 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 5 Jun 2023 14:17:35 +0100 Subject: [PATCH 2/4] Add the cleanup before changing the state --- Lib/test/test_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c4d7c0be92f815..4f5db4c0d85d9f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2760,8 +2760,8 @@ def x(self): ... issubclass(1, BadPG) def test_isinstance_checks_not_at_whim_of_gc(self): - gc.disable() self.addCleanup(gc.enable) + gc.disable() with self.assertRaisesRegex( TypeError, From 99ec54cc60b62e0f16eb2751bde1e40021eab43b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 5 Jun 2023 14:24:24 +0100 Subject: [PATCH 3/4] don't needlessly break compatibility with typing_extensions --- Lib/typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index 884c2230e93b76..29359e9d9a7499 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1780,7 +1780,7 @@ def __new__(mcls, name, bases, namespace, /, **kwargs): base in {object, Generic} or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, {}) or ( - isinstance(base, _ProtocolMeta) + issubclass(base, Generic) and getattr(base, "_is_protocol", False) ) ): From 55368cd8124af622a02f37a51c9baeabc54cde3e Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 5 Jun 2023 14:25:37 +0100 Subject: [PATCH 4/4] nit --- Lib/typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index 29359e9d9a7499..49d0698b8b5e5e 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1778,7 +1778,7 @@ def __new__(mcls, name, bases, namespace, /, **kwargs): for base in bases: if not ( base in {object, Generic} - or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, {}) + or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, []) or ( issubclass(base, Generic) and getattr(base, "_is_protocol", False) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy