mirror of https://github.com/python/cpython
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).
This commit is contained in:
parent
084023ccbe
commit
f9433fff47
|
@ -230,7 +230,7 @@ class InitVar:
|
||||||
self.type = type
|
self.type = type
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if isinstance(self.type, type) and not isinstance(self.type, GenericAlias):
|
if isinstance(self.type, type):
|
||||||
type_name = self.type.__name__
|
type_name = self.type.__name__
|
||||||
else:
|
else:
|
||||||
# typing objects, e.g. List[int]
|
# typing objects, e.g. List[int]
|
||||||
|
@ -1248,7 +1248,7 @@ def _is_dataclass_instance(obj):
|
||||||
def is_dataclass(obj):
|
def is_dataclass(obj):
|
||||||
"""Returns True if obj is a dataclass or an instance of a
|
"""Returns True if obj is a dataclass or an instance of a
|
||||||
dataclass."""
|
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)
|
return hasattr(cls, _FIELDS)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -843,12 +843,11 @@ def singledispatch(func):
|
||||||
return get_origin(cls) in {Union, types.UnionType}
|
return get_origin(cls) in {Union, types.UnionType}
|
||||||
|
|
||||||
def _is_valid_dispatch_type(cls):
|
def _is_valid_dispatch_type(cls):
|
||||||
if isinstance(cls, type) and not isinstance(cls, GenericAlias):
|
if isinstance(cls, type):
|
||||||
return True
|
return True
|
||||||
from typing import get_args
|
from typing import get_args
|
||||||
return (_is_union_type(cls) and
|
return (_is_union_type(cls) and
|
||||||
all(isinstance(arg, type) and not isinstance(arg, GenericAlias)
|
all(isinstance(arg, type) for arg in get_args(cls)))
|
||||||
for arg in get_args(cls)))
|
|
||||||
|
|
||||||
def register(cls, func=None):
|
def register(cls, func=None):
|
||||||
"""generic_func.register(cls, func) -> func
|
"""generic_func.register(cls, func) -> func
|
||||||
|
|
22
Lib/pydoc.py
22
Lib/pydoc.py
|
@ -70,7 +70,6 @@ import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import time
|
import time
|
||||||
import tokenize
|
import tokenize
|
||||||
import types
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import warnings
|
import warnings
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
@ -92,16 +91,13 @@ def pathdirs():
|
||||||
normdirs.append(normdir)
|
normdirs.append(normdir)
|
||||||
return dirs
|
return dirs
|
||||||
|
|
||||||
def _isclass(object):
|
|
||||||
return inspect.isclass(object) and not isinstance(object, types.GenericAlias)
|
|
||||||
|
|
||||||
def _findclass(func):
|
def _findclass(func):
|
||||||
cls = sys.modules.get(func.__module__)
|
cls = sys.modules.get(func.__module__)
|
||||||
if cls is None:
|
if cls is None:
|
||||||
return None
|
return None
|
||||||
for name in func.__qualname__.split('.')[:-1]:
|
for name in func.__qualname__.split('.')[:-1]:
|
||||||
cls = getattr(cls, name)
|
cls = getattr(cls, name)
|
||||||
if not _isclass(cls):
|
if not inspect.isclass(cls):
|
||||||
return None
|
return None
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
@ -109,7 +105,7 @@ def _finddoc(obj):
|
||||||
if inspect.ismethod(obj):
|
if inspect.ismethod(obj):
|
||||||
name = obj.__func__.__name__
|
name = obj.__func__.__name__
|
||||||
self = obj.__self__
|
self = obj.__self__
|
||||||
if (_isclass(self) and
|
if (inspect.isclass(self) and
|
||||||
getattr(getattr(self, name, None), '__func__') is obj.__func__):
|
getattr(getattr(self, name, None), '__func__') is obj.__func__):
|
||||||
# classmethod
|
# classmethod
|
||||||
cls = self
|
cls = self
|
||||||
|
@ -123,7 +119,7 @@ def _finddoc(obj):
|
||||||
elif inspect.isbuiltin(obj):
|
elif inspect.isbuiltin(obj):
|
||||||
name = obj.__name__
|
name = obj.__name__
|
||||||
self = obj.__self__
|
self = obj.__self__
|
||||||
if (_isclass(self) and
|
if (inspect.isclass(self) and
|
||||||
self.__qualname__ + '.' + name == obj.__qualname__):
|
self.__qualname__ + '.' + name == obj.__qualname__):
|
||||||
# classmethod
|
# classmethod
|
||||||
cls = self
|
cls = self
|
||||||
|
@ -210,7 +206,7 @@ def classname(object, modname):
|
||||||
|
|
||||||
def isdata(object):
|
def isdata(object):
|
||||||
"""Check if an object is of a type that probably means it's data."""
|
"""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.isroutine(object) or inspect.isframe(object) or
|
||||||
inspect.istraceback(object) or inspect.iscode(object))
|
inspect.istraceback(object) or inspect.iscode(object))
|
||||||
|
|
||||||
|
@ -481,7 +477,7 @@ class Doc:
|
||||||
# by lacking a __name__ attribute) and an instance.
|
# by lacking a __name__ attribute) and an instance.
|
||||||
try:
|
try:
|
||||||
if inspect.ismodule(object): return self.docmodule(*args)
|
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)
|
if inspect.isroutine(object): return self.docroutine(*args)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
@ -783,7 +779,7 @@ class HTMLDoc(Doc):
|
||||||
modules = inspect.getmembers(object, inspect.ismodule)
|
modules = inspect.getmembers(object, inspect.ismodule)
|
||||||
|
|
||||||
classes, cdict = [], {}
|
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__ exists, believe it. Otherwise use old heuristic.
|
||||||
if (all is not None or
|
if (all is not None or
|
||||||
(inspect.getmodule(value) or object) is object):
|
(inspect.getmodule(value) or object) is object):
|
||||||
|
@ -1223,7 +1219,7 @@ location listed above.
|
||||||
result = result + self.section('DESCRIPTION', desc)
|
result = result + self.section('DESCRIPTION', desc)
|
||||||
|
|
||||||
classes = []
|
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__ exists, believe it. Otherwise use old heuristic.
|
||||||
if (all is not None
|
if (all is not None
|
||||||
or (inspect.getmodule(value) or object) is object):
|
or (inspect.getmodule(value) or object) is object):
|
||||||
|
@ -1707,7 +1703,7 @@ def describe(thing):
|
||||||
return 'member descriptor %s.%s.%s' % (
|
return 'member descriptor %s.%s.%s' % (
|
||||||
thing.__objclass__.__module__, thing.__objclass__.__name__,
|
thing.__objclass__.__module__, thing.__objclass__.__name__,
|
||||||
thing.__name__)
|
thing.__name__)
|
||||||
if _isclass(thing):
|
if inspect.isclass(thing):
|
||||||
return 'class ' + thing.__name__
|
return 'class ' + thing.__name__
|
||||||
if inspect.isfunction(thing):
|
if inspect.isfunction(thing):
|
||||||
return 'function ' + thing.__name__
|
return 'function ' + thing.__name__
|
||||||
|
@ -1768,7 +1764,7 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
|
||||||
desc += ' in module ' + module.__name__
|
desc += ' in module ' + module.__name__
|
||||||
|
|
||||||
if not (inspect.ismodule(object) or
|
if not (inspect.ismodule(object) or
|
||||||
_isclass(object) or
|
inspect.isclass(object) or
|
||||||
inspect.isroutine(object) or
|
inspect.isroutine(object) or
|
||||||
inspect.isdatadescriptor(object) or
|
inspect.isdatadescriptor(object) or
|
||||||
_getdoc(object)):
|
_getdoc(object)):
|
||||||
|
|
|
@ -80,7 +80,7 @@ def resolve_bases(bases):
|
||||||
updated = False
|
updated = False
|
||||||
shift = 0
|
shift = 0
|
||||||
for i, base in enumerate(bases):
|
for i, base in enumerate(bases):
|
||||||
if isinstance(base, type) and not isinstance(base, GenericAlias):
|
if isinstance(base, type):
|
||||||
continue
|
continue
|
||||||
if not hasattr(base, "__mro_entries__"):
|
if not hasattr(base, "__mro_entries__"):
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1079,7 +1079,7 @@ class TypeVarTuple(_Final, _Immutable, _PickleUsingNameMixin, _root=True):
|
||||||
var_tuple_index = None
|
var_tuple_index = None
|
||||||
fillarg = None
|
fillarg = None
|
||||||
for k, arg in enumerate(args):
|
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)
|
subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
|
||||||
if subargs and len(subargs) == 2 and subargs[-1] is ...:
|
if subargs and len(subargs) == 2 and subargs[-1] is ...:
|
||||||
if var_tuple_index is not None:
|
if var_tuple_index is not None:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:class:`types.GenericAlias` no longer relays the ``__class__`` attribute.
|
||||||
|
For example, ``isinstance(list[int], type)`` no longer returns ``True``.
|
|
@ -583,6 +583,7 @@ ga_vectorcall(PyObject *self, PyObject *const *args,
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* const attr_exceptions[] = {
|
static const char* const attr_exceptions[] = {
|
||||||
|
"__class__",
|
||||||
"__origin__",
|
"__origin__",
|
||||||
"__args__",
|
"__args__",
|
||||||
"__unpacked__",
|
"__unpacked__",
|
||||||
|
|
Loading…
Reference in New Issue