def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType) or is_named_instance( right, 'builtins.type'): for item in left.items(): if is_subtype(item, right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return False return True elif isinstance(right, UnboundType): return True elif isinstance(right, TypeType): # All the items must have the same type object status, so # it's sufficient to query only (any) one of them. # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and is_subtype(left.items()[0].ret_type, right.item) else: return False
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[CallableType] for item in t.items(): new_item = item.accept(self) assert isinstance(new_item, CallableType) items.append(new_item) return Overloaded(items)
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[CallableType] for item in t.items(): new = item.accept(self) if isinstance(new, CallableType): items.append(new) else: raise RuntimeError('CallableType expected, but got {}'.format(type(new))) return Overloaded(items=items)
def visit_overloaded(self, t: Overloaded) -> Type: # TODO: Implement a better algorithm that covers at least the same cases # as TypeJoinVisitor.visit_overloaded(). s = self.s if isinstance(s, FunctionLike): if s.items() == t.items(): return Overloaded(t.items()) elif is_subtype(s, t): return s elif is_subtype(t, s): return t else: return meet_types(t.fallback, s.fallback) elif isinstance(self.s, Instance) and self.s.type.is_protocol: call = unpack_callback_protocol(self.s) if call: return meet_types(t, call) return meet_types(t.fallback, s)
def infer_against_overloaded(self, overloaded: Overloaded, template: Callable) -> List[Constraint]: # Create constraints by matching an overloaded type against a template. # This is tricky to do in general. We cheat by only matching against # the first overload item, and by only matching the return type. This # seems to work somewhat well, but we should really use a more # reliable technique. item = overloaded.items()[0] return infer_constraints(template.ret_type, item.ret_type, self.direction)
def find_matching_overload_item(overloaded: Overloaded, template: CallableType) -> CallableType: """Disambiguate overload item against a template.""" items = overloaded.items() for item in items: # Return type may be indeterminate in the template, so ignore it when performing a # subtype check. if mypy.subtypes.is_callable_subtype(item, template, ignore_return=True): return item # Fall back to the first item if we can't find a match. This is totally arbitrary -- # maybe we should just bail out at this point. return items[0]
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType) or is_named_instance(right, "builtins.type"): for item in left.items(): if is_subtype(item, right, self.check_type_parameter): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter): return False return True elif isinstance(right, UnboundType): return True else: return False
def visit_overloaded(self, t: Overloaded) -> Type: # This is more complex than most other cases. Here are some # examples that illustrate how this works. # # First let's define a concise notation: # - Cn are callable types (for n in 1, 2, ...) # - Ov(C1, C2, ...) is an overloaded type with items C1, C2, ... # - Callable[[T, ...], S] is written as [T, ...] -> S. # # We want some basic properties to hold (assume Cn are all # unrelated via Any-similarity): # # join(Ov(C1, C2), C1) == C1 # join(Ov(C1, C2), Ov(C1, C2)) == Ov(C1, C2) # join(Ov(C1, C2), Ov(C1, C3)) == C1 # join(Ov(C2, C2), C3) == join of fallback types # # The presence of Any types makes things more interesting. The join is the # most general type we can get with respect to Any: # # join(Ov([int] -> int, [str] -> str), [Any] -> str) == Any -> str # # We could use a simplification step that removes redundancies, but that's not # implemented right now. Consider this example, where we get a redundancy: # # join(Ov([int, Any] -> Any, [str, Any] -> Any), [Any, int] -> Any) == # Ov([Any, int] -> Any, [Any, int] -> Any) # # TODO: Consider more cases of callable subtyping. result = [] # type: List[CallableType] s = self.s if isinstance(s, FunctionLike): # The interesting case where both types are function types. for t_item in t.items(): for s_item in s.items(): if is_similar_callables(t_item, s_item): if is_equivalent(t_item, s_item): result.append(combine_similar_callables(t_item, s_item)) elif is_subtype(t_item, s_item): result.append(s_item) if result: # TODO: Simplify redundancies from the result. if len(result) == 1: return result[0] else: return Overloaded(result) return join_types(t.fallback, s.fallback) elif isinstance(s, Instance) and s.type.is_protocol: call = unpack_callback_protocol(s) if call: return join_types(t, call) return join_types(t.fallback, s)
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if is_named_instance(right, 'builtins.object'): return True elif isinstance(right, Callable) or is_named_instance( right, 'builtins.type'): for item in left.items(): if is_subtype(item, right): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i]): return False return True elif isinstance(right, UnboundType): return True else: return False
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType) or is_named_instance( right, 'builtins.type'): for item in left.items(): if is_subtype(item, right, self.check_type_parameter): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter): return False return True elif isinstance(right, UnboundType): return True else: return False
def find_matching_overload_item(overloaded: Overloaded, template: CallableType) -> CallableType: """Disambiguate overload item against a template.""" items = overloaded.items() for item in items: # Return type may be indeterminate in the template, so ignore it when performing a # subtype check. if mypy.subtypes.is_callable_subtype(item, template, ignore_return=True): return item # Fall back to the first item if we can't find a match. This is totally arbitrary -- # maybe we should just bail out at this point. return items[0]
def visit_overloaded(self, t: Overloaded) -> Type: # This is more complex than most other cases. Here are some # examples that illustrate how this works. # # First let's define a concise notation: # - Cn are callable types (for n in 1, 2, ...) # - Ov(C1, C2, ...) is an overloaded type with items C1, C2, ... # - Callable[[T, ...], S] is written as [T, ...] -> S. # # We want some basic properties to hold (assume Cn are all # unrelated via Any-similarity): # # join(Ov(C1, C2), C1) == C1 # join(Ov(C1, C2), Ov(C1, C2)) == Ov(C1, C2) # join(Ov(C1, C2), Ov(C1, C3)) == C1 # join(Ov(C2, C2), C3) == join of fallback types # # The presence of Any types makes things more interesting. The join is the # most general type we can get with respect to Any: # # join(Ov([int] -> int, [str] -> str), [Any] -> str) == Any -> str # # We could use a simplification step that removes redundancies, but that's not # implemented right now. Consider this example, where we get a redundancy: # # join(Ov([int, Any] -> Any, [str, Any] -> Any), [Any, int] -> Any) == # Ov([Any, int] -> Any, [Any, int] -> Any) # # TODO: Consider more cases of callable subtyping. result = [] # type: List[CallableType] s = self.s if isinstance(s, FunctionLike): # The interesting case where both types are function types. for t_item in t.items(): for s_item in s.items(): if is_similar_callables(t_item, s_item): if is_equivalent(t_item, s_item): result.append( combine_similar_callables(t_item, s_item)) elif is_subtype(t_item, s_item): result.append(s_item) if result: # TODO: Simplify redundancies from the result. if len(result) == 1: return result[0] else: return Overloaded(result) return join_types(t.fallback, s.fallback) return join_types(t.fallback, s)
def visit_overloaded(self, t: Overloaded) -> None: for item in t.items(): item.accept(self)
def visit_overloaded(self, typ: Overloaded) -> List[str]: triggers = [] for item in typ.items(): triggers.extend(get_type_triggers(item)) return triggers
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[CallableType] for item in t.items(): items.append(cast(CallableType, item.accept(self))) return Overloaded(items)
def visit_overloaded(self, t: Overloaded) -> None: for item in t.items(): item.accept(self)
def visit_overloaded(self, t: Overloaded) -> None: self.traverse_types(t.items())
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType): for item in left.items(): if is_subtype(item, right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True return False elif isinstance(right, Overloaded): # Ensure each overload in the right side (the supertype) is accounted for. previous_match_left_index = -1 matched_overloads = set() possible_invalid_overloads = set() for right_index, right_item in enumerate(right.items()): found_match = False for left_index, left_item in enumerate(left.items()): subtype_match = is_subtype(left_item, right_item, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names) # Order matters: we need to make sure that the index of # this item is at least the index of the previous one. if subtype_match and previous_match_left_index <= left_index: if not found_match: # Update the index of the previous match. previous_match_left_index = left_index found_match = True matched_overloads.add(left_item) possible_invalid_overloads.discard(left_item) else: # If this one overlaps with the supertype in any way, but it wasn't # an exact match, then it's a potential error. if (is_callable_subtype(left_item, right_item, ignore_return=True, ignore_pos_arg_names=self.ignore_pos_arg_names) or is_callable_subtype(right_item, left_item, ignore_return=True, ignore_pos_arg_names=self.ignore_pos_arg_names)): # If this is an overload that's already been matched, there's no # problem. if left_item not in matched_overloads: possible_invalid_overloads.add(left_item) if not found_match: return False if possible_invalid_overloads: # There were potentially invalid overloads that were never matched to the # supertype. return False return True elif isinstance(right, UnboundType): return True elif isinstance(right, TypeType): # All the items must have the same type object status, so # it's sufficient to query only (any) one of them. # This is unsound, we don't check all the __init__ signatures. return left.is_type_obj() and is_subtype(left.items()[0], right) else: return False
def visit_overloaded(self, typ: Overloaded) -> List[str]: triggers = [] for item in typ.items(): triggers.extend(get_type_triggers(item)) return triggers
def visit_overloaded(self, typ: Overloaded) -> SnapshotItem: return ('Overloaded', snapshot_types(typ.items()))
def visit_overloaded(self, template: Overloaded) -> List[Constraint]: res = [] # type: List[Constraint] for t in template.items(): res.extend(infer_constraints(t, self.actual, self.direction)) return res
def visit_overloaded(self, left: Overloaded) -> bool: if isinstance(self.right, Overloaded): return is_same_types(left.items(), self.right.items()) else: return False
def visit_overloaded(self, template: Overloaded) -> List[Constraint]: res = [] # type: List[Constraint] for t in template.items(): res.extend(infer_constraints(t, self.actual, self.direction)) return res
def visit_overloaded(self, t: Overloaded) -> T: return self.query_types(t.items())
def visit_overloaded(self, t: Overloaded) -> T: return self.query_types(t.items())
def visit_overloaded(self, t: types.Overloaded) -> Set[str]: return self._visit(t.items()) | self._visit(t.fallback)
def visit_overloaded(self, typ: Overloaded) -> SnapshotItem: return ('Overloaded', snapshot_types(typ.items()))
def visit_overloaded(self, t: Overloaded) -> None: self.traverse_types(t.items())
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[Callable] for item in t.items(): items.append(cast(Callable, item.accept(self))) return Overloaded(items)
def visit_overloaded(self, t: Overloaded) -> None: for item in t.items(): item.accept(self) # Fallback can be None for overloaded types that haven't been semantically analyzed. if t.fallback is not None: t.fallback.accept(self)
def visit_overloaded(self, t: Overloaded) -> None: for ct in t.items(): ct.accept(self)
def visit_overloaded(self, t: Overloaded) -> Type: return t.items()[0].accept(self)
def visit_overloaded(self, left: Overloaded) -> bool: if isinstance(self.right, Overloaded): return is_same_types(left.items(), self.right.items()) else: return False
def visit_overloaded(self, t: Overloaded) -> None: for item in t.items(): item.accept(self) # Fallback can be None for overloaded types that haven't been semantically analyzed. if t.fallback is not None: t.fallback.accept(self)
def visit_overloaded(self, t: Overloaded) -> Type: return t.items()[0].accept(self)
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType): for item in left.items(): if is_subtype(item, right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True return False elif isinstance(right, Overloaded): # Ensure each overload in the right side (the supertype) is accounted for. previous_match_left_index = -1 matched_overloads = set() possible_invalid_overloads = set() for right_index, right_item in enumerate(right.items()): found_match = False for left_index, left_item in enumerate(left.items()): subtype_match = is_subtype( left_item, right_item, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names) # Order matters: we need to make sure that the index of # this item is at least the index of the previous one. if subtype_match and previous_match_left_index <= left_index: if not found_match: # Update the index of the previous match. previous_match_left_index = left_index found_match = True matched_overloads.add(left_item) possible_invalid_overloads.discard(left_item) else: # If this one overlaps with the supertype in any way, but it wasn't # an exact match, then it's a potential error. if (is_callable_compatible( left_item, right_item, is_compat=is_subtype, ignore_return=True, ignore_pos_arg_names=self.ignore_pos_arg_names) or is_callable_compatible( right_item, left_item, is_compat=is_subtype, ignore_return=True, ignore_pos_arg_names=self. ignore_pos_arg_names)): # If this is an overload that's already been matched, there's no # problem. if left_item not in matched_overloads: possible_invalid_overloads.add(left_item) if not found_match: return False if possible_invalid_overloads: # There were potentially invalid overloads that were never matched to the # supertype. return False return True elif isinstance(right, UnboundType): return True elif isinstance(right, TypeType): # All the items must have the same type object status, so # it's sufficient to query only (any) one of them. # This is unsound, we don't check all the __init__ signatures. return left.is_type_obj() and is_subtype(left.items()[0], right) else: return False
def visit_overloaded(self, t: Overloaded) -> None: for ct in t.items(): ct.accept(self)
def visit_overloaded(self, t: types.Overloaded) -> Set[str]: return self._visit(t.items()) | self._visit(t.fallback)