Skip to content

Commit 2b6932a

Browse files
authored
Speed improvements for typing (#439)
* Inline _geqv and _gorg * Fix typo * Cut MROs in half * Avoid recursion on _gorg * Fix lint * Fxi lint (for realz) * Refactor erasure logic to make it clearer (and probably faster) * Remove redundant check * Remove unnecessary comment * Backport first part to Python 2 * Backport second part to Python 2 * Keep only obvious speed-ups and drop more aggressive optiomizations * Fix _Protocol on Python 2 * Address CR
1 parent b11fbed commit 2b6932a

File tree

2 files changed

+56
-97
lines changed

2 files changed

+56
-97
lines changed

python2/typing.py

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ def _type_check(arg, msg):
357357
if (
358358
type(arg).__name__ in ('_Union', '_Optional') and
359359
not getattr(arg, '__origin__', None) or
360-
isinstance(arg, TypingMeta) and _gorg(arg) in (Generic, _Protocol)
360+
isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol)
361361
):
362362
raise TypeError("Plain %s is not valid as type argument" % arg)
363363
return arg
@@ -945,29 +945,6 @@ def __getitem__(self, arg):
945945
Optional = _Optional(_root=True)
946946

947947

948-
def _gorg(a):
949-
"""Return the farthest origin of a generic class (internal helper)."""
950-
assert isinstance(a, GenericMeta)
951-
while a.__origin__ is not None:
952-
a = a.__origin__
953-
return a
954-
955-
956-
def _geqv(a, b):
957-
"""Return whether two generic classes are equivalent (internal helper).
958-
959-
The intention is to consider generic class X and any of its
960-
parameterized forms (X[T], X[int], etc.) as equivalent.
961-
962-
However, X is not equivalent to a subclass of X.
963-
964-
The relation is reflexive, symmetric and transitive.
965-
"""
966-
assert isinstance(a, GenericMeta) and isinstance(b, GenericMeta)
967-
# Reduce each to its origin.
968-
return _gorg(a) is _gorg(b)
969-
970-
971948
def _next_in_mro(cls):
972949
"""Helper for Generic.__new__.
973950
@@ -977,7 +954,7 @@ def _next_in_mro(cls):
977954
next_in_mro = object
978955
# Look for the last occurrence of Generic or Generic[...].
979956
for i, c in enumerate(cls.__mro__[:-1]):
980-
if isinstance(c, GenericMeta) and _gorg(c) is Generic:
957+
if isinstance(c, GenericMeta) and c._gorg is Generic:
981958
next_in_mro = cls.__mro__[i + 1]
982959
return next_in_mro
983960

@@ -1078,13 +1055,15 @@ def __new__(cls, name, bases, namespace,
10781055
extra = namespace.get('__extra__')
10791056
if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
10801057
bases = (extra,) + bases
1081-
bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b for b in bases)
1058+
bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases)
10821059

10831060
# remove bare Generic from bases if there are other generic bases
10841061
if any(isinstance(b, GenericMeta) and b is not Generic for b in bases):
10851062
bases = tuple(b for b in bases if b is not Generic)
10861063
namespace.update({'__origin__': origin, '__extra__': extra})
10871064
self = super(GenericMeta, cls).__new__(cls, name, bases, namespace)
1065+
super(GenericMeta, self).__setattr__('_gorg',
1066+
self if not origin else origin._gorg)
10881067

10891068
self.__parameters__ = tvars
10901069
# Be prepared that GenericMeta will be subclassed by TupleMeta
@@ -1131,7 +1110,7 @@ def __init__(self, *args, **kwargs):
11311110
def _abc_negative_cache(self):
11321111
if isinstance(self.__extra__, abc.ABCMeta):
11331112
return self.__extra__._abc_negative_cache
1134-
return _gorg(self)._abc_generic_negative_cache
1113+
return self._gorg._abc_generic_negative_cache
11351114

11361115
@_abc_negative_cache.setter
11371116
def _abc_negative_cache(self, value):
@@ -1145,7 +1124,7 @@ def _abc_negative_cache(self, value):
11451124
def _abc_negative_cache_version(self):
11461125
if isinstance(self.__extra__, abc.ABCMeta):
11471126
return self.__extra__._abc_negative_cache_version
1148-
return _gorg(self)._abc_generic_negative_cache_version
1127+
return self._gorg._abc_generic_negative_cache_version
11491128

11501129
@_abc_negative_cache_version.setter
11511130
def _abc_negative_cache_version(self, value):
@@ -1195,7 +1174,7 @@ def _subs_tree(self, tvars=None, args=None):
11951174
if self.__origin__ is None:
11961175
return self
11971176
tree_args = _subs_tree(self, tvars, args)
1198-
return (_gorg(self),) + tuple(tree_args)
1177+
return (self._gorg,) + tuple(tree_args)
11991178

12001179
def __eq__(self, other):
12011180
if not isinstance(other, GenericMeta):
@@ -1211,7 +1190,7 @@ def __hash__(self):
12111190
def __getitem__(self, params):
12121191
if not isinstance(params, tuple):
12131192
params = (params,)
1214-
if not params and not _gorg(self) is Tuple:
1193+
if not params and self._gorg is not Tuple:
12151194
raise TypeError(
12161195
"Parameter list to %s[...] cannot be empty" % _qualname(self))
12171196
msg = "Parameters to generic types must be types."
@@ -1287,7 +1266,7 @@ def __setattr__(self, attr, value):
12871266
):
12881267
super(GenericMeta, self).__setattr__(attr, value)
12891268
else:
1290-
super(GenericMeta, _gorg(self)).__setattr__(attr, value)
1269+
super(GenericMeta, self._gorg).__setattr__(attr, value)
12911270

12921271

12931272
# Prevent checks for Generic to crash when defining Generic.
@@ -1300,7 +1279,7 @@ def _generic_new(base_cls, cls, *args, **kwds):
13001279
if cls.__origin__ is None:
13011280
return base_cls.__new__(cls)
13021281
else:
1303-
origin = _gorg(cls)
1282+
origin = cls._gorg
13041283
obj = base_cls.__new__(origin)
13051284
try:
13061285
obj.__orig_class__ = cls
@@ -1335,7 +1314,7 @@ def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
13351314
__slots__ = ()
13361315

13371316
def __new__(cls, *args, **kwds):
1338-
if _geqv(cls, Generic):
1317+
if cls._gorg is Generic:
13391318
raise TypeError("Type Generic cannot be instantiated; "
13401319
"it can be used only as a base class")
13411320
return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
@@ -1357,7 +1336,7 @@ class TupleMeta(GenericMeta):
13571336

13581337
@_tp_cache
13591338
def __getitem__(self, parameters):
1360-
if self.__origin__ is not None or not _geqv(self, Tuple):
1339+
if self.__origin__ is not None or self._gorg is not Tuple:
13611340
# Normal generic rules apply if this is not the first subscription
13621341
# or a subscription of a subclass.
13631342
return super(TupleMeta, self).__getitem__(parameters)
@@ -1401,7 +1380,7 @@ class Tuple(tuple):
14011380
__slots__ = ()
14021381

14031382
def __new__(cls, *args, **kwds):
1404-
if _geqv(cls, Tuple):
1383+
if cls._gorg is Tuple:
14051384
raise TypeError("Type Tuple cannot be instantiated; "
14061385
"use tuple() instead")
14071386
return _generic_new(tuple, cls, *args, **kwds)
@@ -1416,7 +1395,7 @@ def __repr__(self):
14161395
return self._tree_repr(self._subs_tree())
14171396

14181397
def _tree_repr(self, tree):
1419-
if _gorg(self) is not Callable:
1398+
if self._gorg is not Callable:
14201399
return super(CallableMeta, self)._tree_repr(tree)
14211400
# For actual Callable (not its subclass) we override
14221401
# super(CallableMeta, self)._tree_repr() for nice formatting.
@@ -1436,7 +1415,7 @@ def __getitem__(self, parameters):
14361415
with hashable arguments to improve speed.
14371416
"""
14381417

1439-
if self.__origin__ is not None or not _geqv(self, Callable):
1418+
if self.__origin__ is not None or self._gorg is not Callable:
14401419
return super(CallableMeta, self).__getitem__(parameters)
14411420
if not isinstance(parameters, tuple) or len(parameters) != 2:
14421421
raise TypeError("Callable must be used as "
@@ -1480,7 +1459,7 @@ class Callable(object):
14801459
__slots__ = ()
14811460

14821461
def __new__(cls, *args, **kwds):
1483-
if _geqv(cls, Callable):
1462+
if cls._gorg is Callable:
14841463
raise TypeError("Type Callable cannot be instantiated; "
14851464
"use a non-abstract subclass instead")
14861465
return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
@@ -1530,7 +1509,7 @@ def no_type_check(arg):
15301509
if isinstance(arg, type):
15311510
arg_attrs = arg.__dict__.copy()
15321511
for attr, val in arg.__dict__.items():
1533-
if val in arg.__bases__:
1512+
if val in arg.__bases__ + (arg,):
15341513
arg_attrs.pop(attr)
15351514
for obj in arg_attrs.values():
15361515
if isinstance(obj, types.FunctionType):
@@ -1647,6 +1626,7 @@ def _get_protocol_attrs(self):
16471626
if (not attr.startswith('_abc_') and
16481627
attr != '__abstractmethods__' and
16491628
attr != '_is_protocol' and
1629+
attr != '_gorg' and
16501630
attr != '__dict__' and
16511631
attr != '__args__' and
16521632
attr != '__slots__' and
@@ -1798,7 +1778,7 @@ class List(list, MutableSequence[T]):
17981778
__extra__ = list
17991779

18001780
def __new__(cls, *args, **kwds):
1801-
if _geqv(cls, List):
1781+
if cls._gorg is List:
18021782
raise TypeError("Type List cannot be instantiated; "
18031783
"use list() instead")
18041784
return _generic_new(list, cls, *args, **kwds)
@@ -1809,7 +1789,7 @@ class Deque(collections.deque, MutableSequence[T]):
18091789
__extra__ = collections.deque
18101790

18111791
def __new__(cls, *args, **kwds):
1812-
if _geqv(cls, Deque):
1792+
if cls._gorg is Deque:
18131793
return collections.deque(*args, **kwds)
18141794
return _generic_new(collections.deque, cls, *args, **kwds)
18151795

@@ -1819,7 +1799,7 @@ class Set(set, MutableSet[T]):
18191799
__extra__ = set
18201800

18211801
def __new__(cls, *args, **kwds):
1822-
if _geqv(cls, Set):
1802+
if cls._gorg is Set:
18231803
raise TypeError("Type Set cannot be instantiated; "
18241804
"use set() instead")
18251805
return _generic_new(set, cls, *args, **kwds)
@@ -1830,7 +1810,7 @@ class FrozenSet(frozenset, AbstractSet[T_co]):
18301810
__extra__ = frozenset
18311811

18321812
def __new__(cls, *args, **kwds):
1833-
if _geqv(cls, FrozenSet):
1813+
if cls._gorg is FrozenSet:
18341814
raise TypeError("Type FrozenSet cannot be instantiated; "
18351815
"use frozenset() instead")
18361816
return _generic_new(frozenset, cls, *args, **kwds)
@@ -1887,7 +1867,7 @@ class Dict(dict, MutableMapping[KT, VT]):
18871867
__extra__ = dict
18881868

18891869
def __new__(cls, *args, **kwds):
1890-
if _geqv(cls, Dict):
1870+
if cls._gorg is Dict:
18911871
raise TypeError("Type Dict cannot be instantiated; "
18921872
"use dict() instead")
18931873
return _generic_new(dict, cls, *args, **kwds)
@@ -1898,7 +1878,7 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]):
18981878
__extra__ = collections.defaultdict
18991879

19001880
def __new__(cls, *args, **kwds):
1901-
if _geqv(cls, DefaultDict):
1881+
if cls._gorg is DefaultDict:
19021882
return collections.defaultdict(*args, **kwds)
19031883
return _generic_new(collections.defaultdict, cls, *args, **kwds)
19041884

@@ -1908,7 +1888,7 @@ class Counter(collections.Counter, Dict[T, int]):
19081888
__extra__ = collections.Counter
19091889

19101890
def __new__(cls, *args, **kwds):
1911-
if _geqv(cls, Counter):
1891+
if cls._gorg is Counter:
19121892
return collections.Counter(*args, **kwds)
19131893
return _generic_new(collections.Counter, cls, *args, **kwds)
19141894

@@ -1927,7 +1907,7 @@ class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]):
19271907
__extra__ = _G_base
19281908

19291909
def __new__(cls, *args, **kwds):
1930-
if _geqv(cls, Generator):
1910+
if cls._gorg is Generator:
19311911
raise TypeError("Type Generator cannot be instantiated; "
19321912
"create a subclass instead")
19331913
return _generic_new(_G_base, cls, *args, **kwds)

0 commit comments

Comments
 (0)