]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-89828: Do not relay the __class__ attribute in GenericAlias (#93754)
authorSerhiy Storchaka <storchaka@gmail.com>
Sat, 18 Jun 2022 08:34:57 +0000 (11:34 +0300)
committerGitHub <noreply@github.com>
Sat, 18 Jun 2022 08:34:57 +0000 (11:34 +0300)
list[int].__class__ returned type, and isinstance(list[int], type)
returned True. It caused numerous problems in code that checks
isinstance(x, type).

Lib/dataclasses.py
Lib/functools.py
Lib/pydoc.py
Lib/types.py
Lib/typing.py
Misc/NEWS.d/next/Core and Builtins/2022-06-12-19-31-56.gh-issue-89828.bq02M7.rst [new file with mode: 0644]
Objects/genericaliasobject.c

index 18ab69053fd5206bde8129e5854d0c730c607473..69cab8c563ea9869671720a520a1685fe43a592f 100644 (file)
@@ -230,7 +230,7 @@ class InitVar:
         self.type = type
 
     def __repr__(self):
-        if isinstance(self.type, type) and not isinstance(self.type, GenericAlias):
+        if isinstance(self.type, type):
             type_name = self.type.__name__
         else:
             # typing objects, e.g. List[int]
@@ -1248,7 +1248,7 @@ def _is_dataclass_instance(obj):
 def is_dataclass(obj):
     """Returns True if obj is a dataclass or an instance of a
     dataclass."""
-    cls = obj if isinstance(obj, type) and not isinstance(obj, GenericAlias) else type(obj)
+    cls = obj if isinstance(obj, type) else type(obj)
     return hasattr(cls, _FIELDS)
 
 
index cd5666dfa71fd0cba7563fa9fbad76611ad99846..43ead512e1ea4e63c2b82c4eb3f8db75d4e1eb92 100644 (file)
@@ -843,12 +843,11 @@ def singledispatch(func):
         return get_origin(cls) in {Union, types.UnionType}
 
     def _is_valid_dispatch_type(cls):
-        if isinstance(cls, type) and not isinstance(cls, GenericAlias):
+        if isinstance(cls, type):
             return True
         from typing import get_args
         return (_is_union_type(cls) and
-                all(isinstance(arg, type) and not isinstance(arg, GenericAlias)
-                    for arg in get_args(cls)))
+                all(isinstance(arg, type) for arg in get_args(cls)))
 
     def register(cls, func=None):
         """generic_func.register(cls, func) -> func
index cec9ac89f1cc8d9108bc482f1a14348700e9c063..e96cacbe434454f9a73acb36e35a656579190ab1 100755 (executable)
@@ -70,7 +70,6 @@ import sys
 import sysconfig
 import time
 import tokenize
-import types
 import urllib.parse
 import warnings
 from collections import deque
@@ -92,16 +91,13 @@ def pathdirs():
             normdirs.append(normdir)
     return dirs
 
-def _isclass(object):
-    return inspect.isclass(object) and not isinstance(object, types.GenericAlias)
-
 def _findclass(func):
     cls = sys.modules.get(func.__module__)
     if cls is None:
         return None
     for name in func.__qualname__.split('.')[:-1]:
         cls = getattr(cls, name)
-    if not _isclass(cls):
+    if not inspect.isclass(cls):
         return None
     return cls
 
@@ -109,7 +105,7 @@ def _finddoc(obj):
     if inspect.ismethod(obj):
         name = obj.__func__.__name__
         self = obj.__self__
-        if (_isclass(self) and
+        if (inspect.isclass(self) and
             getattr(getattr(self, name, None), '__func__') is obj.__func__):
             # classmethod
             cls = self
@@ -123,7 +119,7 @@ def _finddoc(obj):
     elif inspect.isbuiltin(obj):
         name = obj.__name__
         self = obj.__self__
-        if (_isclass(self) and
+        if (inspect.isclass(self) and
             self.__qualname__ + '.' + name == obj.__qualname__):
             # classmethod
             cls = self
@@ -210,7 +206,7 @@ def classname(object, modname):
 
 def isdata(object):
     """Check if an object is of a type that probably means it's data."""
-    return not (inspect.ismodule(object) or _isclass(object) or
+    return not (inspect.ismodule(object) or inspect.isclass(object) or
                 inspect.isroutine(object) or inspect.isframe(object) or
                 inspect.istraceback(object) or inspect.iscode(object))
 
@@ -481,7 +477,7 @@ class Doc:
         # by lacking a __name__ attribute) and an instance.
         try:
             if inspect.ismodule(object): return self.docmodule(*args)
-            if _isclass(object): return self.docclass(*args)
+            if inspect.isclass(object): return self.docclass(*args)
             if inspect.isroutine(object): return self.docroutine(*args)
         except AttributeError:
             pass
@@ -783,7 +779,7 @@ class HTMLDoc(Doc):
         modules = inspect.getmembers(object, inspect.ismodule)
 
         classes, cdict = [], {}
-        for key, value in inspect.getmembers(object, _isclass):
+        for key, value in inspect.getmembers(object, inspect.isclass):
             # if __all__ exists, believe it.  Otherwise use old heuristic.
             if (all is not None or
                 (inspect.getmodule(value) or object) is object):
@@ -1223,7 +1219,7 @@ location listed above.
             result = result + self.section('DESCRIPTION', desc)
 
         classes = []
-        for key, value in inspect.getmembers(object, _isclass):
+        for key, value in inspect.getmembers(object, inspect.isclass):
             # if __all__ exists, believe it.  Otherwise use old heuristic.
             if (all is not None
                 or (inspect.getmodule(value) or object) is object):
@@ -1707,7 +1703,7 @@ def describe(thing):
         return 'member descriptor %s.%s.%s' % (
             thing.__objclass__.__module__, thing.__objclass__.__name__,
             thing.__name__)
-    if _isclass(thing):
+    if inspect.isclass(thing):
         return 'class ' + thing.__name__
     if inspect.isfunction(thing):
         return 'function ' + thing.__name__
@@ -1768,7 +1764,7 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
         desc += ' in module ' + module.__name__
 
     if not (inspect.ismodule(object) or
-              _isclass(object) or
+              inspect.isclass(object) or
               inspect.isroutine(object) or
               inspect.isdatadescriptor(object) or
               _getdoc(object)):
index 9490da7b9ee3b9911a24285c628823fc68b182d6..2e73fbc45013372a8ced6d5c2ec41c4e1656d1b7 100644 (file)
@@ -80,7 +80,7 @@ def resolve_bases(bases):
     updated = False
     shift = 0
     for i, base in enumerate(bases):
-        if isinstance(base, type) and not isinstance(base, GenericAlias):
+        if isinstance(base, type):
             continue
         if not hasattr(base, "__mro_entries__"):
             continue
index 25cae7ffb8d788bb75b8d311c028b449e28293d9..1ebc3ce1f64bd61e5fa1ca488a93a0926d8e3625 100644 (file)
@@ -1079,7 +1079,7 @@ class TypeVarTuple(_Final, _Immutable, _PickleUsingNameMixin, _root=True):
         var_tuple_index = None
         fillarg = None
         for k, arg in enumerate(args):
-            if not (isinstance(arg, type) and not isinstance(arg, GenericAlias)):
+            if not isinstance(arg, type):
                 subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
                 if subargs and len(subargs) == 2 and subargs[-1] is ...:
                     if var_tuple_index is not None:
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-12-19-31-56.gh-issue-89828.bq02M7.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-12-19-31-56.gh-issue-89828.bq02M7.rst
new file mode 100644 (file)
index 0000000..14ca99e
--- /dev/null
@@ -0,0 +1,2 @@
+:class:`types.GenericAlias` no longer relays the ``__class__`` attribute.
+For example, ``isinstance(list[int], type)`` no longer returns ``True``.
index 0a0d0cc4c15b68d823d411d39e8a0e2f02278709..b2636d5475dbb952d4bca5b55862c5f1a487e16c 100644 (file)
@@ -583,6 +583,7 @@ ga_vectorcall(PyObject *self, PyObject *const *args,
 }
 
 static const char* const attr_exceptions[] = {
+    "__class__",
     "__origin__",
     "__args__",
     "__unpacked__",