forked from python/mypy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindirection.py
More file actions
107 lines (82 loc) · 4.01 KB
/
indirection.py
File metadata and controls
107 lines (82 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from typing import Dict, Iterable, List, Optional, Set, Union
from mypy.types import TypeVisitor
import mypy.types as types
from mypy.util import split_module_names
def extract_module_names(type_name: Optional[str]) -> List[str]:
"""Returns the module names of a fully qualified type name."""
if type_name is not None:
# Discard the first one, which is just the qualified name of the type
possible_module_names = split_module_names(type_name)
return possible_module_names[1:]
else:
return []
class TypeIndirectionVisitor(TypeVisitor[Set[str]]):
"""Returns all module references within a particular type."""
def __init__(self) -> None:
self.cache = {} # type: Dict[types.Type, Set[str]]
self.seen_aliases = set() # type: Set[types.TypeAliasType]
def find_modules(self, typs: Iterable[types.Type]) -> Set[str]:
self.seen_aliases.clear()
return self._visit(typs)
def _visit(self, typ_or_typs: Union[types.Type, Iterable[types.Type]]) -> Set[str]:
typs = [typ_or_typs] if isinstance(typ_or_typs, types.Type) else typ_or_typs
output = set() # type: Set[str]
for typ in typs:
if isinstance(typ, types.TypeAliasType):
# Avoid infinite recursion for recursive type aliases.
if typ in self.seen_aliases:
continue
self.seen_aliases.add(typ)
if typ in self.cache:
modules = self.cache[typ]
else:
modules = typ.accept(self)
self.cache[typ] = set(modules)
output.update(modules)
return output
def visit_unbound_type(self, t: types.UnboundType) -> Set[str]:
return self._visit(t.args)
def visit_any(self, t: types.AnyType) -> Set[str]:
return set()
def visit_none_type(self, t: types.NoneType) -> Set[str]:
return set()
def visit_uninhabited_type(self, t: types.UninhabitedType) -> Set[str]:
return set()
def visit_erased_type(self, t: types.ErasedType) -> Set[str]:
return set()
def visit_deleted_type(self, t: types.DeletedType) -> Set[str]:
return set()
def visit_type_var(self, t: types.TypeVarType) -> Set[str]:
return self._visit(t.values) | self._visit(t.upper_bound)
def visit_instance(self, t: types.Instance) -> Set[str]:
out = self._visit(t.args)
if t.type:
# Uses of a class depend on everything in the MRO,
# as changes to classes in the MRO can add types to methods,
# change property types, change the MRO itself, etc.
for s in t.type.mro:
out.update(split_module_names(s.module_name))
if t.type.metaclass_type is not None:
out.update(split_module_names(t.type.metaclass_type.type.module_name))
return out
def visit_callable_type(self, t: types.CallableType) -> Set[str]:
out = self._visit(t.arg_types) | self._visit(t.ret_type)
if t.definition is not None:
out.update(extract_module_names(t.definition.fullname))
return out
def visit_overloaded(self, t: types.Overloaded) -> Set[str]:
return self._visit(t.items()) | self._visit(t.fallback)
def visit_tuple_type(self, t: types.TupleType) -> Set[str]:
return self._visit(t.items) | self._visit(t.partial_fallback)
def visit_typeddict_type(self, t: types.TypedDictType) -> Set[str]:
return self._visit(t.items.values()) | self._visit(t.fallback)
def visit_literal_type(self, t: types.LiteralType) -> Set[str]:
return self._visit(t.fallback)
def visit_union_type(self, t: types.UnionType) -> Set[str]:
return self._visit(t.items)
def visit_partial_type(self, t: types.PartialType) -> Set[str]:
return set()
def visit_type_type(self, t: types.TypeType) -> Set[str]:
return self._visit(t.item)
def visit_type_alias_type(self, t: types.TypeAliasType) -> Set[str]:
return self._visit(types.get_proper_type(t))