Skip to content

Commit fcb2856

Browse files
bpo-40397: Remove __args__ and __parameters__ from _SpecialGenericAlias (GH-19984)
1 parent 85bdec1 commit fcb2856

File tree

2 files changed

+89
-80
lines changed

2 files changed

+89
-80
lines changed

Lib/typing.py

+87-80
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,13 @@ def _collect_type_vars(types):
186186
return tuple(tvars)
187187

188188

189-
def _check_generic(cls, parameters):
189+
def _check_generic(cls, parameters, elen):
190190
"""Check correct count for parameters of a generic cls (internal helper).
191191
This gives a nice error message in case of count mismatch.
192192
"""
193-
if not cls.__parameters__:
193+
if not elen:
194194
raise TypeError(f"{cls} is not a generic class")
195195
alen = len(parameters)
196-
elen = len(cls.__parameters__)
197196
if alen != elen:
198197
raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
199198
f" actual {alen}, expected {elen}")
@@ -592,17 +591,6 @@ def __reduce__(self):
592591
return self.__name__
593592

594593

595-
# Special typing constructs Union, Optional, Generic, Callable and Tuple
596-
# use three special attributes for internal bookkeeping of generic types:
597-
# * __parameters__ is a tuple of unique free type parameters of a generic
598-
# type, for example, Dict[T, T].__parameters__ == (T,);
599-
# * __origin__ keeps a reference to a type that was subscripted,
600-
# e.g., Union[T, int].__origin__ == Union, or the non-generic version of
601-
# the type.
602-
# * __args__ is a tuple of all arguments used in subscripting,
603-
# e.g., Dict[T, int].__args__ == (T, int).
604-
605-
606594
def _is_dunder(attr):
607595
return attr.startswith('__') and attr.endswith('__')
608596

@@ -615,28 +603,11 @@ class _BaseGenericAlias(_Final, _root=True):
615603
have 'name' always set. If 'inst' is False, then the alias can't be instantiated,
616604
this is used by e.g. typing.List and typing.Dict.
617605
"""
618-
def __init__(self, origin, params, *, inst=True, name=None):
606+
def __init__(self, origin, *, inst=True, name=None):
619607
self._inst = inst
620608
self._name = name
621-
if not isinstance(params, tuple):
622-
params = (params,)
623609
self.__origin__ = origin
624-
self.__args__ = tuple(... if a is _TypingEllipsis else
625-
() if a is _TypingEmpty else
626-
a for a in params)
627-
self.__parameters__ = _collect_type_vars(params)
628610
self.__slots__ = None # This is not documented.
629-
if not name:
630-
self.__module__ = origin.__module__
631-
632-
def __eq__(self, other):
633-
if not isinstance(other, _BaseGenericAlias):
634-
return NotImplemented
635-
return (self.__origin__ == other.__origin__
636-
and self.__args__ == other.__args__)
637-
638-
def __hash__(self):
639-
return hash((self.__origin__, self.__args__))
640611

641612
def __call__(self, *args, **kwargs):
642613
if not self._inst:
@@ -669,7 +640,7 @@ def __getattr__(self, attr):
669640
raise AttributeError(attr)
670641

671642
def __setattr__(self, attr, val):
672-
if _is_dunder(attr) or attr in ('_name', '_inst'):
643+
if _is_dunder(attr) or attr in ('_name', '_inst', '_nparams'):
673644
super().__setattr__(attr, val)
674645
else:
675646
setattr(self.__origin__, attr, val)
@@ -682,7 +653,38 @@ def __subclasscheck__(self, cls):
682653
" class and instance checks")
683654

684655

656+
# Special typing constructs Union, Optional, Generic, Callable and Tuple
657+
# use three special attributes for internal bookkeeping of generic types:
658+
# * __parameters__ is a tuple of unique free type parameters of a generic
659+
# type, for example, Dict[T, T].__parameters__ == (T,);
660+
# * __origin__ keeps a reference to a type that was subscripted,
661+
# e.g., Union[T, int].__origin__ == Union, or the non-generic version of
662+
# the type.
663+
# * __args__ is a tuple of all arguments used in subscripting,
664+
# e.g., Dict[T, int].__args__ == (T, int).
665+
666+
685667
class _GenericAlias(_BaseGenericAlias, _root=True):
668+
def __init__(self, origin, params, *, inst=True, name=None):
669+
super().__init__(origin, inst=inst, name=name)
670+
if not isinstance(params, tuple):
671+
params = (params,)
672+
self.__args__ = tuple(... if a is _TypingEllipsis else
673+
() if a is _TypingEmpty else
674+
a for a in params)
675+
self.__parameters__ = _collect_type_vars(params)
676+
if not name:
677+
self.__module__ = origin.__module__
678+
679+
def __eq__(self, other):
680+
if not isinstance(other, _GenericAlias):
681+
return NotImplemented
682+
return (self.__origin__ == other.__origin__
683+
and self.__args__ == other.__args__)
684+
685+
def __hash__(self):
686+
return hash((self.__origin__, self.__args__))
687+
686688
@_tp_cache
687689
def __getitem__(self, params):
688690
if self.__origin__ in (Generic, Protocol):
@@ -692,14 +694,14 @@ def __getitem__(self, params):
692694
params = (params,)
693695
msg = "Parameters to generic types must be types."
694696
params = tuple(_type_check(p, msg) for p in params)
695-
_check_generic(self, params)
697+
_check_generic(self, params, len(self.__parameters__))
696698

697699
subst = dict(zip(self.__parameters__, params))
698700
new_args = []
699701
for arg in self.__args__:
700702
if isinstance(arg, TypeVar):
701703
arg = subst[arg]
702-
elif isinstance(arg, (_BaseGenericAlias, GenericAlias)):
704+
elif isinstance(arg, (_GenericAlias, GenericAlias)):
703705
subargs = tuple(subst[x] for x in arg.__parameters__)
704706
arg = arg[subargs]
705707
new_args.append(arg)
@@ -739,11 +741,16 @@ def __mro_entries__(self, bases):
739741
return (self.__origin__,)
740742

741743

744+
# _nparams is the number of accepted parameters, e.g. 0 for Hashable,
745+
# 1 for List and 2 for Dict. It may be -1 if variable number of
746+
# parameters are accepted (needs custom __getitem__).
747+
742748
class _SpecialGenericAlias(_BaseGenericAlias, _root=True):
743-
def __init__(self, origin, params, *, inst=True, name=None):
749+
def __init__(self, origin, nparams, *, inst=True, name=None):
744750
if name is None:
745751
name = origin.__name__
746-
super().__init__(origin, params, inst=inst, name=name)
752+
super().__init__(origin, inst=inst, name=name)
753+
self._nparams = nparams
747754
self.__doc__ = f'A generic version of {origin.__module__}.{origin.__qualname__}'
748755

749756
@_tp_cache
@@ -752,8 +759,7 @@ def __getitem__(self, params):
752759
params = (params,)
753760
msg = "Parameters to generic types must be types."
754761
params = tuple(_type_check(p, msg) for p in params)
755-
_check_generic(self, params)
756-
assert self.__args__ == self.__parameters__
762+
_check_generic(self, params, self._nparams)
757763
return self.copy_with(params)
758764

759765
def copy_with(self, params):
@@ -912,7 +918,7 @@ def __class_getitem__(cls, params):
912918
f"Parameters to {cls.__name__}[...] must all be unique")
913919
else:
914920
# Subscripting a regular Generic subclass.
915-
_check_generic(cls, params)
921+
_check_generic(cls, params, len(cls.__parameters__))
916922
return _GenericAlias(cls, params)
917923

918924
def __init_subclass__(cls, *args, **kwargs):
@@ -1571,18 +1577,18 @@ class Other(Leaf): # Error reported by type checker
15711577
# Various ABCs mimicking those in collections.abc.
15721578
_alias = _SpecialGenericAlias
15731579

1574-
Hashable = _alias(collections.abc.Hashable, ()) # Not generic.
1575-
Awaitable = _alias(collections.abc.Awaitable, T_co)
1576-
Coroutine = _alias(collections.abc.Coroutine, (T_co, T_contra, V_co))
1577-
AsyncIterable = _alias(collections.abc.AsyncIterable, T_co)
1578-
AsyncIterator = _alias(collections.abc.AsyncIterator, T_co)
1579-
Iterable = _alias(collections.abc.Iterable, T_co)
1580-
Iterator = _alias(collections.abc.Iterator, T_co)
1581-
Reversible = _alias(collections.abc.Reversible, T_co)
1582-
Sized = _alias(collections.abc.Sized, ()) # Not generic.
1583-
Container = _alias(collections.abc.Container, T_co)
1584-
Collection = _alias(collections.abc.Collection, T_co)
1585-
Callable = _CallableType(collections.abc.Callable, ())
1580+
Hashable = _alias(collections.abc.Hashable, 0) # Not generic.
1581+
Awaitable = _alias(collections.abc.Awaitable, 1)
1582+
Coroutine = _alias(collections.abc.Coroutine, 3)
1583+
AsyncIterable = _alias(collections.abc.AsyncIterable, 1)
1584+
AsyncIterator = _alias(collections.abc.AsyncIterator, 1)
1585+
Iterable = _alias(collections.abc.Iterable, 1)
1586+
Iterator = _alias(collections.abc.Iterator, 1)
1587+
Reversible = _alias(collections.abc.Reversible, 1)
1588+
Sized = _alias(collections.abc.Sized, 0) # Not generic.
1589+
Container = _alias(collections.abc.Container, 1)
1590+
Collection = _alias(collections.abc.Collection, 1)
1591+
Callable = _CallableType(collections.abc.Callable, 2)
15861592
Callable.__doc__ = \
15871593
"""Callable type; Callable[[int], str] is a function of (int) -> str.
15881594
@@ -1593,15 +1599,16 @@ class Other(Leaf): # Error reported by type checker
15931599
There is no syntax to indicate optional or keyword arguments,
15941600
such function types are rarely used as callback types.
15951601
"""
1596-
AbstractSet = _alias(collections.abc.Set, T_co, name='AbstractSet')
1597-
MutableSet = _alias(collections.abc.MutableSet, T)
1602+
AbstractSet = _alias(collections.abc.Set, 1, name='AbstractSet')
1603+
MutableSet = _alias(collections.abc.MutableSet, 1)
15981604
# NOTE: Mapping is only covariant in the value type.
1599-
Mapping = _alias(collections.abc.Mapping, (KT, VT_co))
1600-
MutableMapping = _alias(collections.abc.MutableMapping, (KT, VT))
1601-
Sequence = _alias(collections.abc.Sequence, T_co)
1602-
MutableSequence = _alias(collections.abc.MutableSequence, T)
1603-
ByteString = _alias(collections.abc.ByteString, ()) # Not generic
1604-
Tuple = _TupleType(tuple, (), inst=False, name='Tuple')
1605+
Mapping = _alias(collections.abc.Mapping, 2)
1606+
MutableMapping = _alias(collections.abc.MutableMapping, 2)
1607+
Sequence = _alias(collections.abc.Sequence, 1)
1608+
MutableSequence = _alias(collections.abc.MutableSequence, 1)
1609+
ByteString = _alias(collections.abc.ByteString, 0) # Not generic
1610+
# Tuple accepts variable number of parameters.
1611+
Tuple = _TupleType(tuple, -1, inst=False, name='Tuple')
16051612
Tuple.__doc__ = \
16061613
"""Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
16071614
@@ -1611,24 +1618,24 @@ class Other(Leaf): # Error reported by type checker
16111618
16121619
To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
16131620
"""
1614-
List = _alias(list, T, inst=False, name='List')
1615-
Deque = _alias(collections.deque, T, name='Deque')
1616-
Set = _alias(set, T, inst=False, name='Set')
1617-
FrozenSet = _alias(frozenset, T_co, inst=False, name='FrozenSet')
1618-
MappingView = _alias(collections.abc.MappingView, T_co)
1619-
KeysView = _alias(collections.abc.KeysView, KT)
1620-
ItemsView = _alias(collections.abc.ItemsView, (KT, VT_co))
1621-
ValuesView = _alias(collections.abc.ValuesView, VT_co)
1622-
ContextManager = _alias(contextlib.AbstractContextManager, T_co, name='ContextManager')
1623-
AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, T_co, name='AsyncContextManager')
1624-
Dict = _alias(dict, (KT, VT), inst=False, name='Dict')
1625-
DefaultDict = _alias(collections.defaultdict, (KT, VT), name='DefaultDict')
1626-
OrderedDict = _alias(collections.OrderedDict, (KT, VT))
1627-
Counter = _alias(collections.Counter, T)
1628-
ChainMap = _alias(collections.ChainMap, (KT, VT))
1629-
Generator = _alias(collections.abc.Generator, (T_co, T_contra, V_co))
1630-
AsyncGenerator = _alias(collections.abc.AsyncGenerator, (T_co, T_contra))
1631-
Type = _alias(type, CT_co, inst=False, name='Type')
1621+
List = _alias(list, 1, inst=False, name='List')
1622+
Deque = _alias(collections.deque, 1, name='Deque')
1623+
Set = _alias(set, 1, inst=False, name='Set')
1624+
FrozenSet = _alias(frozenset, 1, inst=False, name='FrozenSet')
1625+
MappingView = _alias(collections.abc.MappingView, 1)
1626+
KeysView = _alias(collections.abc.KeysView, 1)
1627+
ItemsView = _alias(collections.abc.ItemsView, 2)
1628+
ValuesView = _alias(collections.abc.ValuesView, 1)
1629+
ContextManager = _alias(contextlib.AbstractContextManager, 1, name='ContextManager')
1630+
AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, 1, name='AsyncContextManager')
1631+
Dict = _alias(dict, 2, inst=False, name='Dict')
1632+
DefaultDict = _alias(collections.defaultdict, 2, name='DefaultDict')
1633+
OrderedDict = _alias(collections.OrderedDict, 2)
1634+
Counter = _alias(collections.Counter, 1)
1635+
ChainMap = _alias(collections.ChainMap, 2)
1636+
Generator = _alias(collections.abc.Generator, 3)
1637+
AsyncGenerator = _alias(collections.abc.AsyncGenerator, 2)
1638+
Type = _alias(type, 1, inst=False, name='Type')
16321639
Type.__doc__ = \
16331640
"""A special construct usable to annotate class objects.
16341641
@@ -2122,8 +2129,8 @@ class io:
21222129
io.__name__ = __name__ + '.io'
21232130
sys.modules[io.__name__] = io
21242131

2125-
Pattern = _alias(stdlib_re.Pattern, AnyStr)
2126-
Match = _alias(stdlib_re.Match, AnyStr)
2132+
Pattern = _alias(stdlib_re.Pattern, 1)
2133+
Match = _alias(stdlib_re.Match, 1)
21272134

21282135
class re:
21292136
"""Wrapper namespace for re type aliases."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Removed attributes ``__args__`` and ``__parameters__`` from special generic
2+
aliases like ``typing.List`` (not subscripted).

0 commit comments

Comments
 (0)