forked from python/mypy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtypeops.py
More file actions
491 lines (406 loc) · 19 KB
/
typeops.py
File metadata and controls
491 lines (406 loc) · 19 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
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
"""Miscellaneous type operations and helpers for use during type checking.
NOTE: These must not be accessed from mypy.nodes or mypy.types to avoid import
cycles. These must not be called from the semantic analysis main pass
since these may assume that MROs are ready.
"""
from typing import cast, Optional, List, Sequence, Set
from mypy.types import (
TupleType, Instance, FunctionLike, Type, CallableType, TypeVarDef, Overloaded,
TypeVarType, UninhabitedType, FormalArgument, UnionType, NoneType,
AnyType, TypeOfAny, TypeType, ProperType, LiteralType, get_proper_type, get_proper_types,
copy_type
)
from mypy.nodes import (
FuncBase, FuncItem, OverloadedFuncDef, TypeInfo, TypeVar, ARG_STAR, ARG_STAR2, Expression,
StrExpr, ARG_POS
)
from mypy.maptype import map_instance_to_supertype
from mypy.expandtype import expand_type_by_instance, expand_type
from mypy.sharedparse import argument_elide_name
from mypy.typevars import fill_typevars
from mypy import state
def tuple_fallback(typ: TupleType) -> Instance:
"""Return fallback type for a tuple."""
from mypy.join import join_type_list
info = typ.partial_fallback.type
if info.fullname() != 'builtins.tuple':
return typ.partial_fallback
return Instance(info, [join_type_list(typ.items)])
def type_object_type_from_function(signature: FunctionLike,
info: TypeInfo,
def_info: TypeInfo,
fallback: Instance,
is_new: bool) -> FunctionLike:
# We first need to record all non-trivial (explicit) self types in __init__,
# since they will not be available after we bind them. Note, we use explicit
# self-types only in the defining class, similar to __new__ (but not exactly the same,
# see comment in class_callable below). This is mostly useful for annotating library
# classes such as subprocess.Popen.
default_self = fill_typevars(info)
if not is_new and def_info == info and not info.is_newtype:
orig_self_types = [(it.arg_types[0] if it.arg_types and it.arg_types[0] != default_self
and it.arg_kinds[0] == ARG_POS else None) for it in signature.items()]
else:
orig_self_types = [None] * len(signature.items())
# The __init__ method might come from a generic superclass 'def_info'
# with type variables that do not map identically to the type variables of
# the class 'info' being constructed. For example:
#
# class A(Generic[T]):
# def __init__(self, x: T) -> None: ...
# class B(A[List[T]]):
# ...
#
# We need to map B's __init__ to the type (List[T]) -> None.
signature = bind_self(signature, original_type=default_self, is_classmethod=is_new)
signature = cast(FunctionLike, map_type_from_supertype(signature, info, def_info))
special_sig = None # type: Optional[str]
if def_info.fullname() == 'builtins.dict':
# Special signature!
special_sig = 'dict'
if isinstance(signature, CallableType):
return class_callable(signature, info, fallback, special_sig, is_new, orig_self_types[0])
else:
# Overloaded __init__/__new__.
assert isinstance(signature, Overloaded)
items = [] # type: List[CallableType]
for item, orig_self in zip(signature.items(), orig_self_types):
items.append(class_callable(item, info, fallback, special_sig, is_new, orig_self))
return Overloaded(items)
def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance,
special_sig: Optional[str],
is_new: bool, orig_self_type: Optional[Type] = None) -> CallableType:
"""Create a type object type based on the signature of __init__."""
variables = [] # type: List[TypeVarDef]
variables.extend(info.defn.type_vars)
variables.extend(init_type.variables)
from mypy.subtypes import is_subtype
init_ret_type = get_proper_type(init_type.ret_type)
orig_self_type = get_proper_type(orig_self_type)
default_ret_type = fill_typevars(info)
if (
is_new
and isinstance(init_ret_type, (Instance, TupleType))
# Only use the return type from __new__ if it is actually returning
# a subtype of what we would return otherwise.
and is_subtype(init_ret_type, default_ret_type, ignore_type_params=True)
):
ret_type = init_ret_type # type: Type
elif isinstance(orig_self_type, (Instance, TupleType)):
ret_type = orig_self_type
else:
ret_type = default_ret_type
callable_type = init_type.copy_modified(
ret_type=ret_type, fallback=type_type, name=None, variables=variables,
special_sig=special_sig)
c = callable_type.with_name(info.name())
return c
def map_type_from_supertype(typ: Type,
sub_info: TypeInfo,
super_info: TypeInfo) -> Type:
"""Map type variables in a type defined in a supertype context to be valid
in the subtype context. Assume that the result is unique; if more than
one type is possible, return one of the alternatives.
For example, assume
class D(Generic[S]): ...
class C(D[E[T]], Generic[T]): ...
Now S in the context of D would be mapped to E[T] in the context of C.
"""
# Create the type of self in subtype, of form t[a1, ...].
inst_type = fill_typevars(sub_info)
if isinstance(inst_type, TupleType):
inst_type = tuple_fallback(inst_type)
# Map the type of self to supertype. This gets us a description of the
# supertype type variables in terms of subtype variables, i.e. t[t1, ...]
# so that any type variables in tN are to be interpreted in subtype
# context.
inst_type = map_instance_to_supertype(inst_type, super_info)
# Finally expand the type variables in type with those in the previously
# constructed type. Note that both type and inst_type may have type
# variables, but in type they are interpreted in supertype context while
# in inst_type they are interpreted in subtype context. This works even if
# the names of type variables in supertype and subtype overlap.
return expand_type_by_instance(typ, inst_type)
def supported_self_type(typ: ProperType) -> bool:
"""Is this a supported kind of explicit self-types?
Currently, this means a X or Type[X], where X is an instance or
a type variable with an instance upper bound.
"""
if isinstance(typ, TypeType):
return supported_self_type(typ.item)
return (isinstance(typ, TypeVarType) or
(isinstance(typ, Instance) and typ != fill_typevars(typ.type)))
F = TypeVar('F', bound=FunctionLike)
def bind_self(method: F, original_type: Optional[Type] = None, is_classmethod: bool = False) -> F:
"""Return a copy of `method`, with the type of its first parameter (usually
self or cls) bound to original_type.
If the type of `self` is a generic type (T, or Type[T] for classmethods),
instantiate every occurrence of type with original_type in the rest of the
signature and in the return type.
original_type is the type of E in the expression E.copy(). It is None in
compatibility checks. In this case we treat it as the erasure of the
declared type of self.
This way we can express "the type of self". For example:
T = TypeVar('T', bound='A')
class A:
def copy(self: T) -> T: ...
class B(A): pass
b = B().copy() # type: B
"""
from mypy.infer import infer_type_arguments
if isinstance(method, Overloaded):
return cast(F, Overloaded([bind_self(c, original_type, is_classmethod)
for c in method.items()]))
assert isinstance(method, CallableType)
func = method
if not func.arg_types:
# Invalid method, return something.
return cast(F, func)
if func.arg_kinds[0] == ARG_STAR:
# The signature is of the form 'def foo(*args, ...)'.
# In this case we shouldn't drop the first arg,
# since func will be absorbed by the *args.
# TODO: infer bounds on the type of *args?
return cast(F, func)
self_param_type = get_proper_type(func.arg_types[0])
if func.variables and supported_self_type(self_param_type):
if original_type is None:
# TODO: type check method override (see #7861).
original_type = erase_to_bound(self_param_type)
original_type = get_proper_type(original_type)
ids = [x.id for x in func.variables]
typearg = get_proper_type(infer_type_arguments(ids, self_param_type,
original_type, is_supertype=True)[0])
if (is_classmethod and isinstance(typearg, UninhabitedType)
and isinstance(original_type, (Instance, TypeVarType, TupleType))):
# In case we call a classmethod through an instance x, fallback to type(x)
typearg = get_proper_type(infer_type_arguments(ids, self_param_type,
TypeType(original_type),
is_supertype=True)[0])
def expand(target: Type) -> Type:
assert typearg is not None
return expand_type(target, {func.variables[0].id: typearg})
arg_types = [expand(x) for x in func.arg_types[1:]]
ret_type = expand(func.ret_type)
variables = func.variables[1:]
else:
arg_types = func.arg_types[1:]
ret_type = func.ret_type
variables = func.variables
original_type = get_proper_type(original_type)
if isinstance(original_type, CallableType) and original_type.is_type_obj():
original_type = TypeType.make_normalized(original_type.ret_type)
res = func.copy_modified(arg_types=arg_types,
arg_kinds=func.arg_kinds[1:],
arg_names=func.arg_names[1:],
variables=variables,
ret_type=ret_type,
bound_args=[original_type])
return cast(F, res)
def erase_to_bound(t: Type) -> Type:
# TODO: use value restrictions to produce a union?
t = get_proper_type(t)
if isinstance(t, TypeVarType):
return t.upper_bound
if isinstance(t, TypeType):
if isinstance(t.item, TypeVarType):
return TypeType.make_normalized(t.item.upper_bound)
return t
def callable_corresponding_argument(typ: CallableType,
model: FormalArgument) -> Optional[FormalArgument]:
"""Return the argument a function that corresponds to `model`"""
by_name = typ.argument_by_name(model.name)
by_pos = typ.argument_by_position(model.pos)
if by_name is None and by_pos is None:
return None
if by_name is not None and by_pos is not None:
if by_name == by_pos:
return by_name
# If we're dealing with an optional pos-only and an optional
# name-only arg, merge them. This is the case for all functions
# taking both *args and **args, or a pair of functions like so:
# def right(a: int = ...) -> None: ...
# def left(__a: int = ..., *, a: int = ...) -> None: ...
from mypy.subtypes import is_equivalent
if (not (by_name.required or by_pos.required)
and by_pos.name is None
and by_name.pos is None
and is_equivalent(by_name.typ, by_pos.typ)):
return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False)
return by_name if by_name is not None else by_pos
def make_simplified_union(items: Sequence[Type],
line: int = -1, column: int = -1) -> ProperType:
"""Build union type with redundant union items removed.
If only a single item remains, this may return a non-union type.
Examples:
* [int, str] -> Union[int, str]
* [int, object] -> object
* [int, int] -> int
* [int, Any] -> Union[int, Any] (Any types are not simplified away!)
* [Any, Any] -> Any
Note: This must NOT be used during semantic analysis, since TypeInfos may not
be fully initialized.
"""
items = get_proper_types(items)
while any(isinstance(typ, UnionType) for typ in items):
all_items = [] # type: List[ProperType]
for typ in items:
if isinstance(typ, UnionType):
all_items.extend(typ.items)
else:
all_items.append(typ)
items = all_items
from mypy.subtypes import is_proper_subtype
removed = set() # type: Set[int]
for i, ti in enumerate(items):
if i in removed: continue
# Keep track of the truishness info for deleted subtypes which can be relevant
cbt = cbf = False
for j, tj in enumerate(items):
if i != j and is_proper_subtype(tj, ti):
# We found a redundant item in the union.
removed.add(j)
cbt = cbt or tj.can_be_true
cbf = cbf or tj.can_be_false
# if deleted subtypes had more general truthiness, use that
if not ti.can_be_true and cbt:
items[i] = true_or_false(ti)
elif not ti.can_be_false and cbf:
items[i] = true_or_false(ti)
simplified_set = [items[i] for i in range(len(items)) if i not in removed]
return UnionType.make_union(simplified_set, line, column)
def true_only(t: Type) -> ProperType:
"""
Restricted version of t with only True-ish values
"""
t = get_proper_type(t)
if not t.can_be_true:
# All values of t are False-ish, so there are no true values in it
return UninhabitedType(line=t.line, column=t.column)
elif not t.can_be_false:
# All values of t are already True-ish, so true_only is idempotent in this case
return t
elif isinstance(t, UnionType):
# The true version of a union type is the union of the true versions of its components
new_items = [true_only(item) for item in t.items]
return make_simplified_union(new_items, line=t.line, column=t.column)
else:
new_t = copy_type(t)
new_t.can_be_false = False
return new_t
def false_only(t: Type) -> ProperType:
"""
Restricted version of t with only False-ish values
"""
t = get_proper_type(t)
if not t.can_be_false:
if state.strict_optional:
# All values of t are True-ish, so there are no false values in it
return UninhabitedType(line=t.line)
else:
# When strict optional checking is disabled, everything can be
# False-ish since anything can be None
return NoneType(line=t.line)
elif not t.can_be_true:
# All values of t are already False-ish, so false_only is idempotent in this case
return t
elif isinstance(t, UnionType):
# The false version of a union type is the union of the false versions of its components
new_items = [false_only(item) for item in t.items]
return make_simplified_union(new_items, line=t.line, column=t.column)
else:
new_t = copy_type(t)
new_t.can_be_true = False
return new_t
def true_or_false(t: Type) -> ProperType:
"""
Unrestricted version of t with both True-ish and False-ish values
"""
t = get_proper_type(t)
if isinstance(t, UnionType):
new_items = [true_or_false(item) for item in t.items]
return make_simplified_union(new_items, line=t.line, column=t.column)
new_t = copy_type(t)
new_t.can_be_true = new_t.can_be_true_default()
new_t.can_be_false = new_t.can_be_false_default()
return new_t
def erase_def_to_union_or_bound(tdef: TypeVarDef) -> Type:
if tdef.values:
return make_simplified_union(tdef.values)
else:
return tdef.upper_bound
def erase_to_union_or_bound(typ: TypeVarType) -> ProperType:
if typ.values:
return make_simplified_union(typ.values)
else:
return get_proper_type(typ.upper_bound)
def function_type(func: FuncBase, fallback: Instance) -> FunctionLike:
if func.type:
assert isinstance(func.type, FunctionLike)
return func.type
else:
# Implicit type signature with dynamic types.
if isinstance(func, FuncItem):
return callable_type(func, fallback)
else:
# Broken overloads can have self.type set to None.
# TODO: should we instead always set the type in semantic analyzer?
assert isinstance(func, OverloadedFuncDef)
any_type = AnyType(TypeOfAny.from_error)
dummy = CallableType([any_type, any_type],
[ARG_STAR, ARG_STAR2],
[None, None], any_type,
fallback,
line=func.line, is_ellipsis_args=True)
# Return an Overloaded, because some callers may expect that
# an OverloadedFuncDef has an Overloaded type.
return Overloaded([dummy])
def callable_type(fdef: FuncItem, fallback: Instance,
ret_type: Optional[Type] = None) -> CallableType:
# TODO: somewhat unfortunate duplication with prepare_method_signature in semanal
if fdef.info and not fdef.is_static and fdef.arg_names:
self_type = fill_typevars(fdef.info) # type: Type
if fdef.is_class or fdef.name() == '__new__':
self_type = TypeType.make_normalized(self_type)
args = [self_type] + [AnyType(TypeOfAny.unannotated)] * (len(fdef.arg_names)-1)
else:
args = [AnyType(TypeOfAny.unannotated)] * len(fdef.arg_names)
return CallableType(
args,
fdef.arg_kinds,
[None if argument_elide_name(n) else n for n in fdef.arg_names],
ret_type or AnyType(TypeOfAny.unannotated),
fallback,
name=fdef.name(),
line=fdef.line,
column=fdef.column,
implicit=True,
)
def try_getting_str_literals(expr: Expression, typ: Type) -> Optional[List[str]]:
"""If the given expression or type corresponds to a string literal
or a union of string literals, returns a list of the underlying strings.
Otherwise, returns None.
Specifically, this function is guaranteed to return a list with
one or more strings if one one the following is true:
1. 'expr' is a StrExpr
2. 'typ' is a LiteralType containing a string
3. 'typ' is a UnionType containing only LiteralType of strings
"""
typ = get_proper_type(typ)
if isinstance(expr, StrExpr):
return [expr.value]
if isinstance(typ, Instance) and typ.last_known_value is not None:
possible_literals = [typ.last_known_value] # type: List[Type]
elif isinstance(typ, UnionType):
possible_literals = list(typ.items)
else:
possible_literals = [typ]
strings = []
for lit in get_proper_types(possible_literals):
if isinstance(lit, LiteralType) and lit.fallback.type.fullname() == 'builtins.str':
val = lit.value
assert isinstance(val, str)
strings.append(val)
else:
return None
return strings