Repository navigation
Expand file tree
/
Copy pathtypeops.py
More file actions
1438 lines (1229 loc) · 54.1 KB
/
Copy pathtypeops.py
File metadata and controls
1438 lines (1229 loc) · 54.1 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
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""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 __future__ import annotations
import itertools
from collections.abc import Callable, Iterable, Sequence
from typing import Any, TypeVar, cast
import mypy.subtypes
from mypy.checker_state import checker_state
from mypy.copytype import copy_type
from mypy.expandtype import expand_type, expand_type_by_instance
from mypy.lookup import lookup_stdlib_typeinfo
from mypy.maptype import map_instance_to_supertype
from mypy.modules_state import modules_state
from mypy.nodes import (
ARG_POS,
ARG_STAR,
ARG_STAR2,
SYMBOL_FUNCBASE_TYPES,
Decorator,
Expression,
FuncBase,
FuncDef,
FuncItem,
OverloadedFuncDef,
StrExpr,
SymbolNode,
TypeInfo,
Var,
)
from mypy.state import state
from mypy.types import (
ELLIPSIS_TYPE_NAMES,
NOT_IMPLEMENTED_TYPE_NAMES,
SENTINEL_TYPE_NAMES,
AnyType,
CallableType,
ExtraAttrs,
FormalArgument,
FunctionLike,
Instance,
LiteralType,
NoneType,
NormalizedCallableType,
Overloaded,
Parameters,
ParamSpecType,
PartialType,
ProperType,
TupleType,
Type,
TypeAliasType,
TypedDictType,
TypeOfAny,
TypeQuery,
TypeType,
TypeVarLikeType,
TypeVarTupleType,
TypeVarType,
UninhabitedType,
UnionType,
UnpackType,
flatten_nested_unions,
get_proper_type,
get_proper_types,
instance_cache,
remove_dups,
)
from mypy.typestate import BIND_SELF_CACHE_MAX_SIZE, type_state
from mypy.typetraverser import TypeTraverserVisitor
from mypy.typevars import fill_typevars
def is_recursive_pair(s: Type, t: Type) -> bool:
"""Is this a pair of recursive types?
There may be more cases, and we may be forced to use e.g. has_recursive_types()
here, but this function is called in very hot code, so we try to keep it simple
and return True only in cases we know may have problems.
"""
if isinstance(s, TypeAliasType) and s.is_recursive:
return (
isinstance(get_proper_type(t), (Instance, UnionType))
or isinstance(t, TypeAliasType)
and t.is_recursive
# Tuple types are special, they can cause an infinite recursion even if
# the other type is not recursive, because of the tuple fallback that is
# calculated "on the fly".
or isinstance(get_proper_type(s), TupleType)
)
if isinstance(t, TypeAliasType) and t.is_recursive:
return (
isinstance(get_proper_type(s), (Instance, UnionType))
or isinstance(s, TypeAliasType)
and s.is_recursive
# Same as above.
or isinstance(get_proper_type(t), TupleType)
)
return False
def tuple_fallback(typ: TupleType) -> Instance:
"""Return fallback type for a tuple."""
info = typ.partial_fallback.type
if info.fullname != "builtins.tuple":
return typ.partial_fallback
return Instance(
info,
# Note: flattening recursive unions is dangerous, since it can fool recursive
# types optimization in subtypes.py and go into infinite recursion.
[make_simplified_union(get_tuple_item_types(typ), handle_recursive=False)],
extra_attrs=typ.partial_fallback.extra_attrs,
)
def get_tuple_item_types(typ: TupleType) -> list[Type]:
"""Return tuple item types, replacing unpacks with their possible item types."""
items = []
for item in typ.items:
if isinstance(item, UnpackType):
unpacked_type = get_proper_type(item.type)
if isinstance(unpacked_type, TypeVarTupleType):
unpacked_type = get_proper_type(unpacked_type.upper_bound)
if (
isinstance(unpacked_type, Instance)
and unpacked_type.type.fullname == "builtins.tuple"
):
items.append(unpacked_type.args[0])
else:
raise NotImplementedError
else:
items.append(item)
return items
def get_self_type(func: CallableType, def_info: TypeInfo) -> Type | None:
default_self = fill_typevars(def_info)
if isinstance(get_proper_type(func.ret_type), UninhabitedType):
return func.ret_type
elif func.arg_types and func.arg_types[0] != default_self and func.arg_kinds[0] == ARG_POS:
return func.arg_types[0]
else:
return None
def type_object_type(
info: TypeInfo, named_type: Callable[[str], Instance] | None = None
) -> ProperType:
"""Return the type of a type object.
For a generic type G with type variables T and S the type is generally of form
Callable[..., G[T, S]]
where ... are argument types for the __init__/__new__ method (without the self
argument). Also, the fallback type will be 'type' instead of 'function'.
Note: we keep the unused `named_type` argument to avoid breaking plugins.
"""
allow_cache = (
checker_state.type_checker is not None
and checker_state.type_checker.allow_constructor_cache
)
if info.type_object_type is not None:
if allow_cache:
return info.type_object_type
info.type_object_type = None
# We take the type from whichever of __init__ and __new__ is first
# in the MRO, preferring __init__ if there is a tie.
init_method = info.get("__init__")
new_method = info.get("__new__")
if not init_method or not is_valid_constructor(init_method.node):
# Must be an invalid class definition.
return AnyType(TypeOfAny.from_error)
# There *should* always be a __new__ method except the test stubs
# lack it, so just copy init_method in that situation
new_method = new_method or init_method
if not is_valid_constructor(new_method.node):
# Must be an invalid class definition.
return AnyType(TypeOfAny.from_error)
# The two is_valid_constructor() checks ensure this.
assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator))
assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator))
init_index = info.mro.index(init_method.node.info)
new_index = info.mro.index(new_method.node.info)
if info.metaclass_type is not None:
fallback = info.metaclass_type
else:
type_type = lookup_stdlib_typeinfo("builtins.type", modules_state.modules)
fallback = Instance(type_type, [])
if init_index < new_index:
method: FuncBase | Decorator = init_method.node
is_new = False
elif init_index > new_index:
method = new_method.node
is_new = True
else:
if init_method.node.info.fullname == "builtins.object":
# Both are defined by object. But if we've got a bogus
# base class, we can't know for sure, so check for that.
if info.fallback_to_any:
# Construct a universal callable as the prototype.
any_type = AnyType(TypeOfAny.special_form)
if instance_cache.function_type is None:
function_typeinfo = lookup_stdlib_typeinfo(
"builtins.function", modules_state.modules
)
instance_cache.function_type = Instance(function_typeinfo, [])
sig = CallableType(
arg_types=[any_type, any_type],
arg_kinds=[ARG_STAR, ARG_STAR2],
arg_names=["_args", "_kwds"],
ret_type=any_type,
is_bound=True,
fallback=instance_cache.function_type,
)
result: FunctionLike = class_callable(
sig, info, None, fallback, None, is_new=False
)
if allow_cache and state.strict_optional:
info.type_object_type = result
return result
# Otherwise prefer __init__ in a tie. It isn't clear that this
# is the right thing, but __new__ caused problems with
# typeshed (#5647).
method = init_method.node
is_new = False
# Construct callable type based on signature of __init__. Adjust
# return type and insert type arguments.
if isinstance(method, FuncBase):
if isinstance(method, OverloadedFuncDef) and not method.type:
# Do not cache if the type is not ready. Same logic for decorators is
# achieved in early return above because is_valid_constructor() is False.
allow_cache = False
t = function_type(method, fallback)
else:
assert isinstance(method.type, ProperType)
assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this
t = method.type
result = type_object_type_from_function(t, info, method.info, fallback, is_new)
# Tuple constructor in typeshed is imprecise (and precise one is impossible to express),
# so we special-case constructors for tuple types. Note we skip the tuple class itself
# as a micro-optimization, since it is unlikely one would write tuple((1, 2)).
if method.info.fullname == "builtins.tuple" and info.fullname != "builtins.tuple":
assert isinstance(result, CallableType)
result = result.copy_modified(special_sig="tuple")
# Only write cached result is strict_optional=True, otherwise we may get
# inconsistent behaviour because of union simplification.
if allow_cache and state.strict_optional:
info.type_object_type = result
return result
def is_valid_constructor(n: SymbolNode | None) -> bool:
"""Does this node represents a valid constructor method?
This includes normal functions, overloaded functions, and decorators
that return a callable type.
"""
if isinstance(n, SYMBOL_FUNCBASE_TYPES):
return True
if isinstance(n, Decorator):
return isinstance(get_proper_type(n.type), FunctionLike)
return False
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.
if not is_new and not info.is_newtype:
orig_self_types = [get_self_type(it, def_info) 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=fill_typevars(info),
is_classmethod=is_new,
# Explicit instance self annotations have special handling in class_callable(),
# we don't need to bind any type variables in them if they are generic.
ignore_instances=True,
)
signature = cast(FunctionLike, map_type_from_supertype(signature, info, def_info))
special_sig: str | None = None
if def_info.fullname == "builtins.dict":
# Special signature!
special_sig = "dict"
if isinstance(signature, CallableType):
return class_callable(
signature, info, def_info, fallback, special_sig, is_new, orig_self_types[0]
)
else:
# Overloaded __init__/__new__.
assert isinstance(signature, Overloaded)
items: list[CallableType] = []
for item, orig_self in zip(signature.items, orig_self_types):
items.append(
class_callable(item, info, def_info, fallback, special_sig, is_new, orig_self)
)
return Overloaded(items)
def class_callable(
init_type: CallableType,
info: TypeInfo,
def_info: TypeInfo | None,
type_type: Instance,
special_sig: str | None,
is_new: bool,
orig_self_type: Type | None = None,
) -> CallableType:
"""Create a type object type based on the signature of __init__."""
variables: list[TypeVarLikeType] = []
variables.extend(info.defn.type_vars)
variables.extend(init_type.variables)
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)
# Default return type in the class where constructor method was defined.
default_def_ret_type = fill_typevars(def_info) if def_info is not None else default_ret_type
explicit_type = init_ret_type if is_new else orig_self_type
if (
is_new
and explicit_type is not None
# We used to only use the explicit return type of __new__() when it was a subtype
# of the current class. As a result, we may now have a situation like this:
# class C:
# def __new__(cls) -> C: ...
# class D(C): ...
# So we need to ignore the explicit annotation when creating constructor type for D.
and (
isinstance(explicit_type, AnyType)
and explicit_type.type_of_any != TypeOfAny.unannotated
or not mypy.subtypes.is_equivalent(
default_def_ret_type, explicit_type, ignore_type_params=True
)
)
):
ret_type = explicit_type
elif (
isinstance(explicit_type, (Instance, TupleType, UninhabitedType, LiteralType))
# We have to skip protocols, because it can be a subtype of a return type
# by accident. Like `Hashable` is a subtype of `object`. See #11799
and isinstance(default_ret_type, Instance)
and not default_ret_type.type.is_protocol
# Use the declared self in __init__ if it is a subtype of what we would use otherwise.
and mypy.subtypes.is_subtype(explicit_type, default_ret_type, ignore_type_params=True)
):
ret_type = explicit_type
else:
ret_type = default_ret_type
return init_type.copy_modified(
ret_type=ret_type,
fallback=type_type,
name=info.name,
variables=variables,
special_sig=special_sig,
instance_type=default_ret_type,
)
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, allow_callable: bool = True, allow_instances: bool = True
) -> bool:
"""Is this a supported kind of explicit self-types?
Currently, this means an 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)
if allow_callable and isinstance(typ, CallableType):
# Special case: allow class callable instead of Type[...] as cls annotation,
# as well as callable self for callback protocols.
return True
return isinstance(typ, TypeVarType) or (
allow_instances and isinstance(typ, Instance) and typ != fill_typevars(typ.type)
)
F = TypeVar("F", bound=FunctionLike)
def bind_self(
method: F,
original_type: Type | None = None,
is_classmethod: bool = False,
ignore_instances: 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
"""
if isinstance(method, Overloaded):
items = []
# If the original object type has Any, we record the inferred self-types,
if original_type and mypy.subtypes.has_any_type(original_type, ignore_in_type_obj=True):
bound_args: list[Type] | None = []
else:
bound_args = None
for c in method.items:
bound = bind_self_inner(c, original_type, is_classmethod, ignore_instances)
if bound is None:
items.append(c)
bound_args = None
# If some items can't be bound, we ignore them all for simplicity.
continue
func, variables = bound
res = func.copy_modified(
arg_types=func.arg_types[1:],
arg_kinds=func.arg_kinds[1:],
arg_names=func.arg_names[1:],
variables=variables,
is_bound=True,
)
items.append(res)
if bound_args is not None:
bound_args.append(func.arg_types[0])
return cast(F, Overloaded(items, bound_args))
assert isinstance(method, CallableType)
bound = bind_self_inner(method, original_type, is_classmethod, ignore_instances)
if bound is None:
return method
func, variables = bound
res = func.copy_modified(
arg_types=func.arg_types[1:],
arg_kinds=func.arg_kinds[1:],
arg_names=func.arg_names[1:],
variables=variables,
is_bound=True,
)
return cast(F, res)
def bind_self_inner(
func: CallableType,
original_type: Type | None = None,
is_classmethod: bool = False,
ignore_instances: bool = False,
) -> tuple[CallableType, Sequence[TypeVarLikeType]] | None:
"""Implementation of bind_self()."""
if not func.arg_types:
# Invalid method, return something.
return None
if func.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
# 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?
# In the case of **kwargs we should probably emit an error, but
# for now we simply skip it, to avoid crashes down the line.
return None
self_param_type = get_proper_type(func.arg_types[0])
variables: Sequence[TypeVarLikeType]
# Having a def __call__(self: Callable[...], ...) can cause infinite recursion. Although
# this special-casing looks not very principled, there is nothing meaningful we can infer
# from such definition, since it is inherently indefinitely recursive.
allow_callable = func.name is None or not func.name.startswith("__call__ of")
if func.variables and supported_self_type(
self_param_type, allow_callable=allow_callable, allow_instances=not ignore_instances
):
from mypy.infer import infer_type_arguments
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)
# Find which of method type variables appear in the type of "self".
self_ids = {tv.id for tv in get_all_type_vars(self_param_type)}
self_vars = [tv for tv in func.variables if tv.id in self_ids]
cache_key = None
if not any(tv.id.is_meta_var() for tv in self_vars) and state.strict_optional:
# Only use cache for something that in not freshened (e.g. protocol access),
# otherwise it would be pointless as each fresh meta variable is unique.
cache_key = (func, original_type, is_classmethod, ignore_instances)
if cache_key is not None and cache_key in type_state.bind_self_cache:
return type_state.bind_self_cache[cache_key]
# Solve for these type arguments using the actual class or instance type.
typeargs, _ = infer_type_arguments(
self_vars, self_param_type, original_type, is_supertype=True, erase_types=False
)
if (
is_classmethod
and any(isinstance(get_proper_type(t), UninhabitedType) for t in typeargs)
and isinstance(original_type, (Instance, TypeVarType, TupleType))
):
# In case we call a classmethod through an instance x, fallback to type(x).
typeargs, _ = infer_type_arguments(
self_vars,
self_param_type,
TypeType(original_type),
is_supertype=True,
erase_types=False,
)
# Update the method signature with the solutions found.
# Technically, some constraints might be unsolvable, make them Never.
to_apply = [t if t is not None else UninhabitedType() for t in typeargs]
func = expand_type(func, {tv.id: arg for tv, arg in zip(self_vars, to_apply)})
variables = [v for v in func.variables if v not in self_vars]
if cache_key is not None:
if len(type_state.bind_self_cache) > BIND_SELF_CACHE_MAX_SIZE:
type_state.bind_self_cache.clear()
type_state.bind_self_cache[cache_key] = func, variables
else:
variables = func.variables
return func, variables
def bind_self_fast(method: F, original_type: Type | None = None) -> F:
"""Return a copy of `method`, with the type of its first parameter (usually
self or cls) bound to original_type.
This is a faster version of mypy.typeops.bind_self() that can be used for methods
with trivial self/cls annotations.
"""
if isinstance(method, Overloaded):
items = [bind_self_fast(c, original_type) for c in method.items]
return cast(F, Overloaded(items))
assert isinstance(method, CallableType)
if not method.arg_types:
# Invalid method, return something.
return method
if method.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
# See bind_self() for details.
return method
return method.copy_modified(
arg_types=method.arg_types[1:],
arg_kinds=method.arg_kinds[1:],
arg_names=method.arg_names[1:],
is_bound=True,
)
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, is_type_form=t.is_type_form)
return t
def callable_corresponding_argument(
typ: NormalizedCallableType | Parameters, model: FormalArgument
) -> FormalArgument | None:
"""Return the argument of 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(x: int = ..., /, *, a: int = ...) -> None: ...
from mypy.meet import meet_types
if (
not (by_name.required or by_pos.required)
and by_pos.name is None
and by_name.pos is None
# This is not principled, but prevents a crash. It's weird to have a FormalArgument
# that has an UnpackType.
and not isinstance(by_name.typ, UnpackType)
and not isinstance(by_pos.typ, UnpackType)
):
return FormalArgument(
by_name.name, by_pos.pos, meet_types(by_name.typ, by_pos.typ), False
)
return by_name
return by_name if by_name is not None else by_pos
def simple_literal_type(t: ProperType | None) -> Instance | None:
"""Extract the underlying fallback Instance type for a simple Literal"""
if isinstance(t, Instance) and t.last_known_value is not None:
t = t.last_known_value
if isinstance(t, LiteralType):
return t.fallback
return None
def is_simple_literal(t: ProperType) -> bool:
if isinstance(t, LiteralType):
return t.fallback.type.is_enum or t.fallback.type.fullname == "builtins.str"
if isinstance(t, Instance):
return t.last_known_value is not None and isinstance(t.last_known_value.value, str)
return False
def make_simplified_union(
items: Sequence[Type],
line: int = -1,
column: int = -1,
*,
keep_erased: bool = False,
contract_literals: bool = True,
handle_recursive: bool = True,
) -> 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
* [int, Union[bytes, str]] -> Union[int, bytes, str]
Note: This must NOT be used during semantic analysis, since TypeInfos may not
be fully initialized.
The keep_erased flag is used for type inference against union types
containing type variables. If set to True, keep all ErasedType items.
The contract_literals flag indicates whether we need to contract literal types
back into a sum type. Set it to False when called by try_expanding_sum_type_
to_union().
"""
# Step 1: expand all nested unions
items = flatten_nested_unions(items, handle_recursive=handle_recursive)
# Step 2: fast path for single item
if len(items) == 1:
return get_proper_type(items[0])
# Step 3: remove redundant unions
simplified_set: Sequence[Type] = _remove_redundant_union_items(items, keep_erased)
# Step 4: If more than one literal exists in the union, try to simplify
if (
contract_literals
and sum(isinstance(get_proper_type(item), LiteralType) for item in simplified_set) > 1
):
simplified_set = try_contracting_literals_in_union(simplified_set)
result = get_proper_type(UnionType.make_union(simplified_set, line, column))
nitems = len(items)
if nitems > 1 and (
nitems > 2 or not (type(items[0]) is NoneType or type(items[1]) is NoneType)
):
# Step 5: At last, we erase any (inconsistent) extra attributes on instances.
# Initialize with None instead of an empty set as a micro-optimization. The set
# is needed very rarely, so we try to avoid constructing it.
extra_attrs_set: set[ExtraAttrs] | None = None
for item in items:
instance = try_getting_instance_fallback(item)
if instance and instance.extra_attrs:
if extra_attrs_set is None:
extra_attrs_set = {instance.extra_attrs}
else:
extra_attrs_set.add(instance.extra_attrs)
# Code below is awkward, because we don't want the extra checks to affect
# performance in the common case.
erase_extra = False
if extra_attrs_set is not None:
fallback = try_getting_instance_fallback(result)
if fallback is None:
return result
if len(extra_attrs_set) > 1: # This case is too tricky to handle.
erase_extra = True
else:
# Check that all relevant items have the extra attributes.
for item in items:
instance = try_getting_instance_fallback(item)
if instance and instance.type == fallback.type and not instance.extra_attrs:
erase_extra = True
break
if erase_extra:
fallback.extra_attrs = None
return result
def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[Type]:
from mypy.subtypes import is_proper_subtype
# The first pass through this loop, we check if later items are subtypes of earlier items.
# The second pass through this loop, we check if earlier items are subtypes of later items
# (by reversing the remaining items)
for _direction in range(2):
new_items: list[Type] = []
# seen is a map from a type to its index in new_items
seen: dict[ProperType, int] = {}
unduplicated_literal_fallbacks: set[Instance] | None = None
for ti in items:
proper_ti = get_proper_type(ti)
# UninhabitedType is always redundant
if isinstance(proper_ti, UninhabitedType):
continue
duplicate_index = -1
# Quickly check if we've seen this type
if proper_ti in seen:
duplicate_index = seen[proper_ti]
elif (
isinstance(proper_ti, LiteralType)
and unduplicated_literal_fallbacks is not None
and proper_ti.fallback in unduplicated_literal_fallbacks
):
# This is an optimisation for unions with many LiteralType
# We've already checked for exact duplicates. This means that any super type of
# the LiteralType must be a super type of its fallback. If we've gone through
# the expensive loop below and found no super type for a previous LiteralType
# with the same fallback, we can skip doing that work again and just add the type
# to new_items
pass
else:
# If not, check if we've seen a supertype of this type
for j, tj in enumerate(new_items):
proper_tj = get_proper_type(tj)
# If tj is an Instance with a last_known_value, do not remove proper_ti
# (unless it's an instance with the same last_known_value)
if (
isinstance(proper_tj, Instance)
and proper_tj.last_known_value is not None
and not (
isinstance(proper_ti, Instance)
and proper_tj.last_known_value == proper_ti.last_known_value
)
):
continue
if is_proper_subtype(
ti, tj, keep_erased_types=keep_erased, ignore_promotions=True
):
duplicate_index = j
break
if duplicate_index != -1:
# If deleted subtypes had more general truthiness, use that
orig_item = new_items[duplicate_index]
if not orig_item.can_be_true and ti.can_be_true:
new_items[duplicate_index] = true_or_false(orig_item)
elif not orig_item.can_be_false and ti.can_be_false:
new_items[duplicate_index] = true_or_false(orig_item)
else:
# We have a non-duplicate item, add it to new_items
seen[proper_ti] = len(new_items)
new_items.append(ti)
if isinstance(proper_ti, LiteralType):
if unduplicated_literal_fallbacks is None:
unduplicated_literal_fallbacks = set()
unduplicated_literal_fallbacks.add(proper_ti.fallback)
items = new_items
if len(items) <= 1:
break
items.reverse()
return items
def _get_type_method_ret_type(t: ProperType, *, name: str) -> Type | None:
# For Enum literals the ret_type can change based on the Enum
# we need to check the type of the enum rather than the literal
if isinstance(t, LiteralType) and t.is_enum_literal():
t = t.fallback
if isinstance(t, Instance):
sym = t.type.get(name)
if sym:
sym_type = get_proper_type(sym.type)
if isinstance(sym_type, CallableType):
return sym_type.ret_type
return None
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]
can_be_true_items = [item for item in new_items if item.can_be_true]
return make_simplified_union(can_be_true_items, line=t.line, column=t.column)
elif isinstance(t, Instance) and t.type.fullname == "builtins.bool":
return LiteralType(True, fallback=t)
else:
ret_type = _get_type_method_ret_type(t, name="__bool__") or _get_type_method_ret_type(
t, name="__len__"
)
if ret_type and not ret_type.can_be_true:
return UninhabitedType(line=t.line, column=t.column)
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]
can_be_false_items = [item for item in new_items if item.can_be_false]
return make_simplified_union(can_be_false_items, line=t.line, column=t.column)
elif isinstance(t, Instance) and t.type.fullname in ("builtins.str", "builtins.bytes"):
return LiteralType("", fallback=t)
elif isinstance(t, Instance) and t.type.fullname == "builtins.int":
return LiteralType(0, fallback=t)
elif isinstance(t, Instance) and t.type.fullname == "builtins.bool":
return LiteralType(False, fallback=t)
else:
ret_type = _get_type_method_ret_type(t, name="__bool__") or _get_type_method_ret_type(
t, name="__len__"
)
if ret_type:
if not ret_type.can_be_false:
return UninhabitedType(line=t.line)
elif isinstance(t, Instance):
if (t.type.is_final or t.type.is_enum) and state.strict_optional:
return UninhabitedType(line=t.line)
elif isinstance(t, LiteralType) and t.is_enum_literal() and state.strict_optional:
return UninhabitedType(line=t.line)
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: TypeVarLikeType) -> Type:
# TODO(PEP612): fix for ParamSpecType
if isinstance(tdef, ParamSpecType):
return AnyType(TypeOfAny.from_error)
if isinstance(tdef, TypeVarType) and 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:
# Either a broken overload, or decorated overload type is not ready.
# TODO: make sure the caller defers if possible.
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,