-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Expand file tree
/
Copy pathtype_visitor.py
More file actions
373 lines (280 loc) · 11.6 KB
/
type_visitor.py
File metadata and controls
373 lines (280 loc) · 11.6 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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
"""Type visitor classes.
This module defines the type visitors that are intended to be
subclassed by other code. They have been separated out into their own
module to ease converting mypy to run under mypyc, since currently
mypyc-extension classes can extend interpreted classes but not the
other way around. Separating them out, then, allows us to compile
types before we can compile everything that uses a TypeVisitor.
The visitors are all re-exported from mypy.types and that is how
other modules refer to them.
"""
from abc import abstractmethod
from mypy.backports import OrderedDict
from typing import Generic, TypeVar, cast, Any, List, Callable, Iterable, Optional, Set, Sequence
from mypy_extensions import trait, mypyc_attr
T = TypeVar('T')
from mypy.types import (
Type, AnyType, CallableType, Overloaded, TupleType, TypedDictType, LiteralType,
RawExpressionType, Instance, NoneType, TypeType,
UnionType, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarLikeType,
UnboundType, ErasedType, StarType, EllipsisType, TypeList, CallableArgument,
PlaceholderType, TypeAliasType, ParamSpecType, UnpackType, get_proper_type
)
@trait
@mypyc_attr(allow_interpreted_subclasses=True)
class TypeVisitor(Generic[T]):
"""Visitor class for types (Type subclasses).
The parameter T is the return type of the visit methods.
"""
@abstractmethod
def visit_unbound_type(self, t: UnboundType) -> T:
pass
@abstractmethod
def visit_any(self, t: AnyType) -> T:
pass
@abstractmethod
def visit_none_type(self, t: NoneType) -> T:
pass
@abstractmethod
def visit_uninhabited_type(self, t: UninhabitedType) -> T:
pass
@abstractmethod
def visit_erased_type(self, t: ErasedType) -> T:
pass
@abstractmethod
def visit_deleted_type(self, t: DeletedType) -> T:
pass
@abstractmethod
def visit_type_var(self, t: TypeVarType) -> T:
pass
@abstractmethod
def visit_param_spec(self, t: ParamSpecType) -> T:
pass
@abstractmethod
def visit_instance(self, t: Instance) -> T:
pass
@abstractmethod
def visit_callable_type(self, t: CallableType) -> T:
pass
@abstractmethod
def visit_overloaded(self, t: Overloaded) -> T:
pass
@abstractmethod
def visit_tuple_type(self, t: TupleType) -> T:
pass
@abstractmethod
def visit_typeddict_type(self, t: TypedDictType) -> T:
pass
@abstractmethod
def visit_literal_type(self, t: LiteralType) -> T:
pass
@abstractmethod
def visit_union_type(self, t: UnionType) -> T:
pass
@abstractmethod
def visit_partial_type(self, t: PartialType) -> T:
pass
@abstractmethod
def visit_type_type(self, t: TypeType) -> T:
pass
@abstractmethod
def visit_type_alias_type(self, t: TypeAliasType) -> T:
pass
@abstractmethod
def visit_unpack_type(self, t: UnpackType) -> T:
pass
@trait
@mypyc_attr(allow_interpreted_subclasses=True)
class SyntheticTypeVisitor(TypeVisitor[T]):
"""A TypeVisitor that also knows how to visit synthetic AST constructs.
Not just real types."""
@abstractmethod
def visit_star_type(self, t: StarType) -> T:
pass
@abstractmethod
def visit_type_list(self, t: TypeList) -> T:
pass
@abstractmethod
def visit_callable_argument(self, t: CallableArgument) -> T:
pass
@abstractmethod
def visit_ellipsis_type(self, t: EllipsisType) -> T:
pass
@abstractmethod
def visit_raw_expression_type(self, t: RawExpressionType) -> T:
pass
@abstractmethod
def visit_placeholder_type(self, t: PlaceholderType) -> T:
pass
@mypyc_attr(allow_interpreted_subclasses=True)
class TypeTranslator(TypeVisitor[Type]):
"""Identity type transformation.
Subclass this and override some methods to implement a non-trivial
transformation.
"""
def visit_unbound_type(self, t: UnboundType) -> Type:
return t
def visit_any(self, t: AnyType) -> Type:
return t
def visit_none_type(self, t: NoneType) -> Type:
return t
def visit_uninhabited_type(self, t: UninhabitedType) -> Type:
return t
def visit_erased_type(self, t: ErasedType) -> Type:
return t
def visit_deleted_type(self, t: DeletedType) -> Type:
return t
def visit_instance(self, t: Instance) -> Type:
last_known_value: Optional[LiteralType] = None
if t.last_known_value is not None:
raw_last_known_value = t.last_known_value.accept(self)
assert isinstance(raw_last_known_value, LiteralType) # type: ignore
last_known_value = raw_last_known_value
return Instance(
typ=t.type,
args=self.translate_types(t.args),
line=t.line,
column=t.column,
last_known_value=last_known_value,
)
def visit_type_var(self, t: TypeVarType) -> Type:
return t
def visit_param_spec(self, t: ParamSpecType) -> Type:
return t
def visit_partial_type(self, t: PartialType) -> Type:
return t
def visit_unpack_type(self, t: UnpackType) -> Type:
return t.type.accept(self)
def visit_callable_type(self, t: CallableType) -> Type:
return t.copy_modified(arg_types=self.translate_types(t.arg_types),
ret_type=t.ret_type.accept(self),
variables=self.translate_variables(t.variables))
def visit_tuple_type(self, t: TupleType) -> Type:
return TupleType(self.translate_types(t.items),
# TODO: This appears to be unsafe.
cast(Any, t.partial_fallback.accept(self)),
t.line, t.column)
def visit_typeddict_type(self, t: TypedDictType) -> Type:
items = OrderedDict([
(item_name, item_type.accept(self))
for (item_name, item_type) in t.items.items()
])
return TypedDictType(items,
t.required_keys,
# TODO: This appears to be unsafe.
cast(Any, t.fallback.accept(self)),
t.line, t.column)
def visit_literal_type(self, t: LiteralType) -> Type:
fallback = t.fallback.accept(self)
assert isinstance(fallback, Instance) # type: ignore
return LiteralType(
value=t.value,
fallback=fallback,
line=t.line,
column=t.column,
)
def visit_union_type(self, t: UnionType) -> Type:
return UnionType(self.translate_types(t.items), t.line, t.column)
def translate_types(self, types: Iterable[Type]) -> List[Type]:
return [t.accept(self) for t in types]
def translate_variables(self,
variables: Sequence[TypeVarLikeType]) -> Sequence[TypeVarLikeType]:
return variables
def visit_overloaded(self, t: Overloaded) -> Type:
items: List[CallableType] = []
for item in t.items:
new = item.accept(self)
assert isinstance(new, CallableType) # type: ignore
items.append(new)
return Overloaded(items=items)
def visit_type_type(self, t: TypeType) -> Type:
return TypeType.make_normalized(t.item.accept(self), line=t.line, column=t.column)
@abstractmethod
def visit_type_alias_type(self, t: TypeAliasType) -> Type:
# This method doesn't have a default implementation for type translators,
# because type aliases are special: some information is contained in the
# TypeAlias node, and we normally don't generate new nodes. Every subclass
# must implement this depending on its semantics.
pass
@mypyc_attr(allow_interpreted_subclasses=True)
class TypeQuery(SyntheticTypeVisitor[T]):
"""Visitor for performing queries of types.
strategy is used to combine results for a series of types,
common use cases involve a boolean query using `any` or `all`.
Note: this visitor keeps an internal state (tracks type aliases to avoid
recursion), so it should *never* be re-used for querying different types,
create a new visitor instance instead.
# TODO: check that we don't have existing violations of this rule.
"""
def __init__(self, strategy: Callable[[Iterable[T]], T]) -> None:
self.strategy = strategy
# Keep track of the type aliases already visited. This is needed to avoid
# infinite recursion on types like A = Union[int, List[A]].
self.seen_aliases: Set[TypeAliasType] = set()
def visit_unbound_type(self, t: UnboundType) -> T:
return self.query_types(t.args)
def visit_type_list(self, t: TypeList) -> T:
return self.query_types(t.items)
def visit_callable_argument(self, t: CallableArgument) -> T:
return t.typ.accept(self)
def visit_any(self, t: AnyType) -> T:
return self.strategy([])
def visit_uninhabited_type(self, t: UninhabitedType) -> T:
return self.strategy([])
def visit_none_type(self, t: NoneType) -> T:
return self.strategy([])
def visit_erased_type(self, t: ErasedType) -> T:
return self.strategy([])
def visit_deleted_type(self, t: DeletedType) -> T:
return self.strategy([])
def visit_type_var(self, t: TypeVarType) -> T:
return self.query_types([t.upper_bound] + t.values)
def visit_param_spec(self, t: ParamSpecType) -> T:
return self.strategy([])
def visit_unpack_type(self, t: UnpackType) -> T:
return self.query_types([t.type])
def visit_partial_type(self, t: PartialType) -> T:
return self.strategy([])
def visit_instance(self, t: Instance) -> T:
return self.query_types(t.args)
def visit_callable_type(self, t: CallableType) -> T:
# FIX generics
return self.query_types(t.arg_types + [t.ret_type])
def visit_tuple_type(self, t: TupleType) -> T:
return self.query_types(t.items)
def visit_typeddict_type(self, t: TypedDictType) -> T:
return self.query_types(t.items.values())
def visit_raw_expression_type(self, t: RawExpressionType) -> T:
return self.strategy([])
def visit_literal_type(self, t: LiteralType) -> T:
return self.strategy([])
def visit_star_type(self, t: StarType) -> T:
return t.type.accept(self)
def visit_union_type(self, t: UnionType) -> T:
return self.query_types(t.items)
def visit_overloaded(self, t: Overloaded) -> T:
return self.query_types(t.items)
def visit_type_type(self, t: TypeType) -> T:
return t.item.accept(self)
def visit_ellipsis_type(self, t: EllipsisType) -> T:
return self.strategy([])
def visit_placeholder_type(self, t: PlaceholderType) -> T:
return self.query_types(t.args)
def visit_type_alias_type(self, t: TypeAliasType) -> T:
return get_proper_type(t).accept(self)
def query_types(self, types: Iterable[Type]) -> T:
"""Perform a query for a list of types.
Use the strategy to combine the results.
Skip type aliases already visited types to avoid infinite recursion.
"""
res: List[T] = []
for t in types:
if isinstance(t, TypeAliasType):
# Avoid infinite recursion for recursive type aliases.
# TODO: Ideally we should fire subvisitors here (or use caching) if we care
# about duplicates.
if t in self.seen_aliases:
continue
self.seen_aliases.add(t)
res.append(t.accept(self))
return self.strategy(res)