Skip to content

Commit 37218fd

Browse files
committed
Try some aliases speed-up
1 parent 8437cf5 commit 37218fd

File tree

11 files changed

+81
-127
lines changed

11 files changed

+81
-127
lines changed

mypy/constraints.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
ArgKind,
2222
TypeInfo,
2323
)
24+
from mypy.type_visitor import ALL_STRATEGY, BoolTypeQuery
2425
from mypy.types import (
2526
TUPLE_LIKE_INSTANCE_NAMES,
2627
AnyType,
@@ -41,7 +42,6 @@
4142
TypeAliasType,
4243
TypedDictType,
4344
TypeOfAny,
44-
TypeQuery,
4545
TypeType,
4646
TypeVarId,
4747
TypeVarLikeType,
@@ -670,9 +670,9 @@ def is_complete_type(typ: Type) -> bool:
670670
return typ.accept(CompleteTypeVisitor())
671671

672672

673-
class CompleteTypeVisitor(TypeQuery[bool]):
673+
class CompleteTypeVisitor(BoolTypeQuery):
674674
def __init__(self) -> None:
675-
super().__init__(all)
675+
super().__init__(ALL_STRATEGY)
676676

677677
def visit_uninhabited_type(self, t: UninhabitedType) -> bool:
678678
return False

mypy/indirection.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,15 @@ def find_modules(self, typs: Iterable[types.Type]) -> set[str]:
3737
return self.modules
3838

3939
def _visit(self, typ: types.Type) -> None:
40-
if isinstance(typ, types.TypeAliasType):
40+
if isinstance(typ, types.TypeAliasType) and typ.is_recursive:
4141
# Avoid infinite recursion for recursive type aliases.
42-
if typ not in self.seen_aliases:
43-
self.seen_aliases.add(typ)
42+
self.seen_aliases.add(typ)
4443
typ.accept(self)
4544

4645
def _visit_type_tuple(self, typs: tuple[types.Type, ...]) -> None:
4746
# Micro-optimization: Specialized version of _visit for lists
4847
for typ in typs:
49-
if isinstance(typ, types.TypeAliasType):
48+
if isinstance(typ, types.TypeAliasType) and typ.is_recursive:
5049
# Avoid infinite recursion for recursive type aliases.
5150
if typ in self.seen_aliases:
5251
continue
@@ -56,7 +55,7 @@ def _visit_type_tuple(self, typs: tuple[types.Type, ...]) -> None:
5655
def _visit_type_list(self, typs: list[types.Type]) -> None:
5756
# Micro-optimization: Specialized version of _visit for tuples
5857
for typ in typs:
59-
if isinstance(typ, types.TypeAliasType):
58+
if isinstance(typ, types.TypeAliasType) and typ.is_recursive:
6059
# Avoid infinite recursion for recursive type aliases.
6160
if typ in self.seen_aliases:
6261
continue

mypy/semanal_typeargs.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,11 @@ def visit_block(self, o: Block) -> None:
8383

8484
def visit_type_alias_type(self, t: TypeAliasType) -> None:
8585
super().visit_type_alias_type(t)
86-
if t in self.seen_aliases:
87-
# Avoid infinite recursion on recursive type aliases.
88-
# Note: it is fine to skip the aliases we have already seen in non-recursive
89-
# types, since errors there have already been reported.
90-
return
91-
self.seen_aliases.add(t)
86+
if t.is_recursive:
87+
if t in self.seen_aliases:
88+
# Avoid infinite recursion on recursive type aliases.
89+
return
90+
self.seen_aliases.add(t)
9291
assert t.alias is not None, f"Unfixed type alias {t.type_ref}"
9392
is_error, is_invalid = self.validate_args(
9493
t.alias.name, tuple(t.args), t.alias.alias_tvars, t

mypy/stats.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
YieldFromExpr,
4444
)
4545
from mypy.traverser import TraverserVisitor
46+
from mypy.type_visitor import ANY_STRATEGY, BoolTypeQuery
4647
from mypy.typeanal import collect_all_inner_types
4748
from mypy.types import (
4849
AnyType,
@@ -52,7 +53,6 @@
5253
TupleType,
5354
Type,
5455
TypeOfAny,
55-
TypeQuery,
5656
TypeVarType,
5757
get_proper_type,
5858
get_proper_types,
@@ -453,9 +453,9 @@ def is_imprecise(t: Type) -> bool:
453453
return t.accept(HasAnyQuery())
454454

455455

456-
class HasAnyQuery(TypeQuery[bool]):
456+
class HasAnyQuery(BoolTypeQuery):
457457
def __init__(self) -> None:
458-
super().__init__(any)
458+
super().__init__(ANY_STRATEGY)
459459

460460
def visit_any(self, t: AnyType) -> bool:
461461
return not is_special_form_any(t)

mypy/test/testtypes.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,18 +201,6 @@ def test_type_alias_expand_once(self) -> None:
201201
assert get_proper_type(A) == target
202202
assert get_proper_type(target) == target
203203

204-
def test_type_alias_expand_all(self) -> None:
205-
A, _ = self.fx.def_alias_1(self.fx.a)
206-
assert A.expand_all_if_possible() is None
207-
A, _ = self.fx.def_alias_2(self.fx.a)
208-
assert A.expand_all_if_possible() is None
209-
210-
B = self.fx.non_rec_alias(self.fx.a)
211-
C = self.fx.non_rec_alias(TupleType([B, B], Instance(self.fx.std_tuplei, [B])))
212-
assert C.expand_all_if_possible() == TupleType(
213-
[self.fx.a, self.fx.a], Instance(self.fx.std_tuplei, [self.fx.a])
214-
)
215-
216204
def test_recursive_nested_in_non_recursive(self) -> None:
217205
A, _ = self.fx.def_alias_1(self.fx.a)
218206
T = TypeVarType(

mypy/type_visitor.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from abc import abstractmethod
1717
from collections.abc import Iterable, Sequence
18-
from typing import Any, Callable, Final, Generic, TypeVar, cast
18+
from typing import Any, Final, Generic, TypeVar, cast
1919

2020
from mypy_extensions import mypyc_attr, trait
2121

@@ -353,16 +353,19 @@ class TypeQuery(SyntheticTypeVisitor[T]):
353353
# TODO: check that we don't have existing violations of this rule.
354354
"""
355355

356-
def __init__(self, strategy: Callable[[list[T]], T]) -> None:
357-
self.strategy = strategy
356+
def __init__(self) -> None:
358357
# Keep track of the type aliases already visited. This is needed to avoid
359358
# infinite recursion on types like A = Union[int, List[A]].
360-
self.seen_aliases: set[TypeAliasType] = set()
359+
self.seen_aliases: set[TypeAliasType] | None = None
361360
# By default, we eagerly expand type aliases, and query also types in the
362361
# alias target. In most cases this is a desired behavior, but we may want
363362
# to skip targets in some cases (e.g. when collecting type variables).
364363
self.skip_alias_target = False
365364

365+
@abstractmethod
366+
def strategy(self, items: list[T]) -> T:
367+
raise NotImplementedError
368+
366369
def visit_unbound_type(self, t: UnboundType, /) -> T:
367370
return self.query_types(t.args)
368371

@@ -440,14 +443,15 @@ def visit_placeholder_type(self, t: PlaceholderType, /) -> T:
440443
return self.query_types(t.args)
441444

442445
def visit_type_alias_type(self, t: TypeAliasType, /) -> T:
443-
# Skip type aliases already visited types to avoid infinite recursion.
444-
# TODO: Ideally we should fire subvisitors here (or use caching) if we care
445-
# about duplicates.
446-
if t in self.seen_aliases:
447-
return self.strategy([])
448-
self.seen_aliases.add(t)
449446
if self.skip_alias_target:
450447
return self.query_types(t.args)
448+
# Skip type aliases already visited types to avoid infinite recursion.
449+
if t.is_recursive:
450+
if self.seen_aliases is None:
451+
self.seen_aliases = set()
452+
elif t in self.seen_aliases:
453+
return self.strategy([])
454+
self.seen_aliases.add(t)
451455
return get_proper_type(t).accept(self)
452456

453457
def query_types(self, types: Iterable[Type]) -> T:
@@ -580,16 +584,15 @@ def visit_placeholder_type(self, t: PlaceholderType, /) -> bool:
580584
return self.query_types(t.args)
581585

582586
def visit_type_alias_type(self, t: TypeAliasType, /) -> bool:
583-
# Skip type aliases already visited types to avoid infinite recursion.
584-
# TODO: Ideally we should fire subvisitors here (or use caching) if we care
585-
# about duplicates.
586-
if self.seen_aliases is None:
587-
self.seen_aliases = set()
588-
elif t in self.seen_aliases:
589-
return self.default
590-
self.seen_aliases.add(t)
591587
if self.skip_alias_target:
592588
return self.query_types(t.args)
589+
# Skip type aliases already visited types to avoid infinite recursion.
590+
if t.is_recursive:
591+
if self.seen_aliases is None:
592+
self.seen_aliases = set()
593+
elif t in self.seen_aliases:
594+
return self.default
595+
self.seen_aliases.add(t)
593596
return get_proper_type(t).accept(self)
594597

595598
def query_types(self, types: list[Type] | tuple[Type, ...]) -> bool:

mypy/typeanal.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,9 +2377,9 @@ def has_explicit_any(t: Type) -> bool:
23772377
return t.accept(HasExplicitAny())
23782378

23792379

2380-
class HasExplicitAny(TypeQuery[bool]):
2380+
class HasExplicitAny(BoolTypeQuery):
23812381
def __init__(self) -> None:
2382-
super().__init__(any)
2382+
super().__init__(ANY_STRATEGY)
23832383

23842384
def visit_any(self, t: AnyType) -> bool:
23852385
return t.type_of_any == TypeOfAny.explicit
@@ -2418,15 +2418,11 @@ def collect_all_inner_types(t: Type) -> list[Type]:
24182418

24192419

24202420
class CollectAllInnerTypesQuery(TypeQuery[list[Type]]):
2421-
def __init__(self) -> None:
2422-
super().__init__(self.combine_lists_strategy)
2423-
24242421
def query_types(self, types: Iterable[Type]) -> list[Type]:
24252422
return self.strategy([t.accept(self) for t in types]) + list(types)
24262423

2427-
@classmethod
2428-
def combine_lists_strategy(cls, it: Iterable[list[Type]]) -> list[Type]:
2429-
return list(itertools.chain.from_iterable(it))
2424+
def strategy(self, items: Iterable[list[Type]]) -> list[Type]:
2425+
return list(itertools.chain.from_iterable(items))
24302426

24312427

24322428
def make_optional_type(t: Type) -> Type:
@@ -2556,7 +2552,6 @@ def __init__(self, api: SemanticAnalyzerCoreInterface, scope: TypeVarLikeScope)
25562552
self.scope = scope
25572553
self.type_var_likes: list[tuple[str, TypeVarLikeExpr]] = []
25582554
self.has_self_type = False
2559-
self.seen_aliases: set[TypeAliasType] | None = None
25602555
self.include_callables = True
25612556

25622557
def _seems_like_callable(self, type: UnboundType) -> bool:
@@ -2653,7 +2648,8 @@ def visit_union_type(self, t: UnionType) -> None:
26532648
self.process_types(t.items)
26542649

26552650
def visit_overloaded(self, t: Overloaded) -> None:
2656-
self.process_types(t.items) # type: ignore[arg-type]
2651+
for it in t.items:
2652+
it.accept(self)
26572653

26582654
def visit_type_type(self, t: TypeType) -> None:
26592655
t.item.accept(self)
@@ -2665,12 +2661,6 @@ def visit_placeholder_type(self, t: PlaceholderType) -> None:
26652661
return self.process_types(t.args)
26662662

26672663
def visit_type_alias_type(self, t: TypeAliasType) -> None:
2668-
# Skip type aliases in already visited types to avoid infinite recursion.
2669-
if self.seen_aliases is None:
2670-
self.seen_aliases = set()
2671-
elif t in self.seen_aliases:
2672-
return
2673-
self.seen_aliases.add(t)
26742664
self.process_types(t.args)
26752665

26762666
def process_types(self, types: list[Type] | tuple[Type, ...]) -> None:

mypy/typeops.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,12 +1114,12 @@ def get_all_type_vars(tp: Type) -> list[TypeVarLikeType]:
11141114

11151115
class TypeVarExtractor(TypeQuery[list[TypeVarLikeType]]):
11161116
def __init__(self, include_all: bool = False) -> None:
1117-
super().__init__(self._merge)
1117+
super().__init__()
11181118
self.include_all = include_all
11191119

1120-
def _merge(self, iter: Iterable[list[TypeVarLikeType]]) -> list[TypeVarLikeType]:
1120+
def strategy(self, items: Iterable[list[TypeVarLikeType]]) -> list[TypeVarLikeType]:
11211121
out = []
1122-
for item in iter:
1122+
for item in items:
11231123
out.extend(item)
11241124
return out
11251125

0 commit comments

Comments
 (0)