Skip to content

Commit f9433ff

Browse files
authoredJun 18, 2022
gh-89828: Do not relay the __class__ attribute in GenericAlias (#93754)
list[int].__class__ returned type, and isinstance(list[int], type) returned True. It caused numerous problems in code that checks isinstance(x, type).
1 parent 084023c commit f9433ff

File tree

7 files changed

+18
-20
lines changed

7 files changed

+18
-20
lines changed
 

‎Lib/dataclasses.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def __init__(self, type):
230230
self.type = type
231231

232232
def __repr__(self):
233-
if isinstance(self.type, type) and not isinstance(self.type, GenericAlias):
233+
if isinstance(self.type, type):
234234
type_name = self.type.__name__
235235
else:
236236
# typing objects, e.g. List[int]
@@ -1248,7 +1248,7 @@ def _is_dataclass_instance(obj):
12481248
def is_dataclass(obj):
12491249
"""Returns True if obj is a dataclass or an instance of a
12501250
dataclass."""
1251-
cls = obj if isinstance(obj, type) and not isinstance(obj, GenericAlias) else type(obj)
1251+
cls = obj if isinstance(obj, type) else type(obj)
12521252
return hasattr(cls, _FIELDS)
12531253

12541254

‎Lib/functools.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -843,12 +843,11 @@ def _is_union_type(cls):
843843
return get_origin(cls) in {Union, types.UnionType}
844844

845845
def _is_valid_dispatch_type(cls):
846-
if isinstance(cls, type) and not isinstance(cls, GenericAlias):
846+
if isinstance(cls, type):
847847
return True
848848
from typing import get_args
849849
return (_is_union_type(cls) and
850-
all(isinstance(arg, type) and not isinstance(arg, GenericAlias)
851-
for arg in get_args(cls)))
850+
all(isinstance(arg, type) for arg in get_args(cls)))
852851

853852
def register(cls, func=None):
854853
"""generic_func.register(cls, func) -> func

‎Lib/pydoc.py

+9-13
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ class or function within a module or module in a package. If the
7070
import sysconfig
7171
import time
7272
import tokenize
73-
import types
7473
import urllib.parse
7574
import warnings
7675
from collections import deque
@@ -92,24 +91,21 @@ def pathdirs():
9291
normdirs.append(normdir)
9392
return dirs
9493

95-
def _isclass(object):
96-
return inspect.isclass(object) and not isinstance(object, types.GenericAlias)
97-
9894
def _findclass(func):
9995
cls = sys.modules.get(func.__module__)
10096
if cls is None:
10197
return None
10298
for name in func.__qualname__.split('.')[:-1]:
10399
cls = getattr(cls, name)
104-
if not _isclass(cls):
100+
if not inspect.isclass(cls):
105101
return None
106102
return cls
107103

108104
def _finddoc(obj):
109105
if inspect.ismethod(obj):
110106
name = obj.__func__.__name__
111107
self = obj.__self__
112-
if (_isclass(self) and
108+
if (inspect.isclass(self) and
113109
getattr(getattr(self, name, None), '__func__') is obj.__func__):
114110
# classmethod
115111
cls = self
@@ -123,7 +119,7 @@ def _finddoc(obj):
123119
elif inspect.isbuiltin(obj):
124120
name = obj.__name__
125121
self = obj.__self__
126-
if (_isclass(self) and
122+
if (inspect.isclass(self) and
127123
self.__qualname__ + '.' + name == obj.__qualname__):
128124
# classmethod
129125
cls = self
@@ -210,7 +206,7 @@ def classname(object, modname):
210206

211207
def isdata(object):
212208
"""Check if an object is of a type that probably means it's data."""
213-
return not (inspect.ismodule(object) or _isclass(object) or
209+
return not (inspect.ismodule(object) or inspect.isclass(object) or
214210
inspect.isroutine(object) or inspect.isframe(object) or
215211
inspect.istraceback(object) or inspect.iscode(object))
216212

@@ -481,7 +477,7 @@ def document(self, object, name=None, *args):
481477
# by lacking a __name__ attribute) and an instance.
482478
try:
483479
if inspect.ismodule(object): return self.docmodule(*args)
484-
if _isclass(object): return self.docclass(*args)
480+
if inspect.isclass(object): return self.docclass(*args)
485481
if inspect.isroutine(object): return self.docroutine(*args)
486482
except AttributeError:
487483
pass
@@ -783,7 +779,7 @@ def docmodule(self, object, name=None, mod=None, *ignored):
783779
modules = inspect.getmembers(object, inspect.ismodule)
784780

785781
classes, cdict = [], {}
786-
for key, value in inspect.getmembers(object, _isclass):
782+
for key, value in inspect.getmembers(object, inspect.isclass):
787783
# if __all__ exists, believe it. Otherwise use old heuristic.
788784
if (all is not None or
789785
(inspect.getmodule(value) or object) is object):
@@ -1223,7 +1219,7 @@ def docmodule(self, object, name=None, mod=None):
12231219
result = result + self.section('DESCRIPTION', desc)
12241220

12251221
classes = []
1226-
for key, value in inspect.getmembers(object, _isclass):
1222+
for key, value in inspect.getmembers(object, inspect.isclass):
12271223
# if __all__ exists, believe it. Otherwise use old heuristic.
12281224
if (all is not None
12291225
or (inspect.getmodule(value) or object) is object):
@@ -1707,7 +1703,7 @@ def describe(thing):
17071703
return 'member descriptor %s.%s.%s' % (
17081704
thing.__objclass__.__module__, thing.__objclass__.__name__,
17091705
thing.__name__)
1710-
if _isclass(thing):
1706+
if inspect.isclass(thing):
17111707
return 'class ' + thing.__name__
17121708
if inspect.isfunction(thing):
17131709
return 'function ' + thing.__name__
@@ -1768,7 +1764,7 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
17681764
desc += ' in module ' + module.__name__
17691765

17701766
if not (inspect.ismodule(object) or
1771-
_isclass(object) or
1767+
inspect.isclass(object) or
17721768
inspect.isroutine(object) or
17731769
inspect.isdatadescriptor(object) or
17741770
_getdoc(object)):

‎Lib/types.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def resolve_bases(bases):
8080
updated = False
8181
shift = 0
8282
for i, base in enumerate(bases):
83-
if isinstance(base, type) and not isinstance(base, GenericAlias):
83+
if isinstance(base, type):
8484
continue
8585
if not hasattr(base, "__mro_entries__"):
8686
continue

‎Lib/typing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,7 @@ def __typing_prepare_subst__(self, alias, args):
10791079
var_tuple_index = None
10801080
fillarg = None
10811081
for k, arg in enumerate(args):
1082-
if not (isinstance(arg, type) and not isinstance(arg, GenericAlias)):
1082+
if not isinstance(arg, type):
10831083
subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
10841084
if subargs and len(subargs) == 2 and subargs[-1] is ...:
10851085
if var_tuple_index is not None:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:class:`types.GenericAlias` no longer relays the ``__class__`` attribute.
2+
For example, ``isinstance(list[int], type)`` no longer returns ``True``.

‎Objects/genericaliasobject.c

+1
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ ga_vectorcall(PyObject *self, PyObject *const *args,
583583
}
584584

585585
static const char* const attr_exceptions[] = {
586+
"__class__",
586587
"__origin__",
587588
"__args__",
588589
"__unpacked__",

0 commit comments

Comments
 (0)