diff --git a/mypy/checker.py b/mypy/checker.py index ced9a1bd8348..64cf094558f9 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -40,8 +40,8 @@ from mypy.checkmember import map_type_from_supertype, bind_self, erase_to_bound from mypy import messages from mypy.subtypes import ( - is_subtype, is_equivalent, is_proper_subtype, is_more_precise, restrict_subtype_away, - is_subtype_ignoring_tvars + is_subtype, is_equivalent, is_proper_subtype, is_more_precise, + restrict_subtype_away, is_subtype_ignoring_tvars ) from mypy.maptype import map_instance_to_supertype from mypy.semanal import fill_typevars, set_callable_name, refers_to_fullname @@ -835,7 +835,7 @@ def check_inplace_operator_method(self, defn: FuncBase) -> None: def check_getattr_method(self, typ: CallableType, context: Context) -> None: method_type = CallableType([AnyType(), self.named_type('builtins.str')], [nodes.ARG_POS, nodes.ARG_POS], - [None], + [None, None], AnyType(), self.named_type('builtins.function')) if not is_subtype(typ, method_type): @@ -936,7 +936,7 @@ def check_override(self, override: FunctionLike, original: FunctionLike, """ # Use boolean variable to clarify code. fail = False - if not is_subtype(override, original): + if not is_subtype(override, original, ignore_pos_arg_names=True): fail = True elif (not isinstance(original, Overloaded) and isinstance(override, Overloaded) and @@ -1043,7 +1043,7 @@ def check_compatibility(self, name: str, base1: TypeInfo, # Method override first_sig = bind_self(first_type) second_sig = bind_self(second_type) - ok = is_subtype(first_sig, second_sig) + ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True) elif first_type and second_type: ok = is_equivalent(first_type, second_type) else: diff --git a/mypy/messages.py b/mypy/messages.py index 9060923d611a..fb5923dc52c4 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -15,7 +15,7 @@ ) from mypy.nodes import ( TypeInfo, Context, MypyFile, op_methods, FuncDef, reverse_type_aliases, - ARG_STAR, ARG_STAR2 + ARG_POS, ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2 ) @@ -77,6 +77,15 @@ TYPEDDICT_ITEM_NAME_MUST_BE_STRING_LITERAL = \ 'Expected TypedDict item name to be string literal' +ARG_CONSTRUCTOR_NAMES = { + ARG_POS: "Arg", + ARG_OPT: "DefaultArg", + ARG_NAMED: "NamedArg", + ARG_NAMED_OPT: "DefaultNamedArg", + ARG_STAR: "StarArg", + ARG_STAR2: "KwArg", +} + class MessageBuilder: """Helper class for reporting type checker error messages with parameters. @@ -176,8 +185,30 @@ def format(self, typ: Type, verbosity: int = 0) -> str: return_type = strip_quotes(self.format(func.ret_type)) if func.is_ellipsis_args: return 'Callable[..., {}]'.format(return_type) - arg_types = [strip_quotes(self.format(t)) for t in func.arg_types] - return 'Callable[[{}], {}]'.format(", ".join(arg_types), return_type) + arg_strings = [] + for arg_name, arg_type, arg_kind in zip( + func.arg_names, func.arg_types, func.arg_kinds): + if (arg_kind == ARG_POS and arg_name is None + or verbosity == 0 and arg_kind in (ARG_POS, ARG_OPT)): + + arg_strings.append( + strip_quotes( + self.format( + arg_type, + verbosity = max(verbosity - 1, 0)))) + else: + constructor = ARG_CONSTRUCTOR_NAMES[arg_kind] + if arg_kind in (ARG_STAR, ARG_STAR2): + arg_strings.append("{}({})".format( + constructor, + strip_quotes(self.format(arg_type)))) + else: + arg_strings.append("{}('{}', {})".format( + constructor, + arg_name, + strip_quotes(self.format(arg_type)))) + + return 'Callable[[{}], {}]'.format(", ".join(arg_strings), return_type) else: # Use a simple representation for function types; proper # function types may result in long and difficult-to-read diff --git a/mypy/subtypes.py b/mypy/subtypes.py index bbc8e3b0b941..43f28c18d029 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1,7 +1,7 @@ -from typing import List, Dict, Callable +from typing import List, Optional, Dict, Callable from mypy.types import ( - Type, AnyType, UnboundType, TypeVisitor, ErrorType, Void, NoneTyp, + Type, AnyType, UnboundType, TypeVisitor, ErrorType, FormalArgument, Void, NoneTyp, Instance, TypeVarType, CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, TypeList, PartialType, DeletedType, UninhabitedType, TypeType, is_named_instance ) @@ -10,7 +10,10 @@ # Circular import; done in the function instead. # import mypy.solve from mypy import messages, sametypes -from mypy.nodes import CONTRAVARIANT, COVARIANT +from mypy.nodes import ( + CONTRAVARIANT, COVARIANT, + ARG_POS, ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2, +) from mypy.maptype import map_instance_to_supertype from mypy import experiments @@ -29,7 +32,8 @@ def check_type_parameter(lefta: Type, righta: Type, variance: int) -> bool: def is_subtype(left: Type, right: Type, - type_parameter_checker: TypeParameterChecker = check_type_parameter) -> bool: + type_parameter_checker: TypeParameterChecker = check_type_parameter, + *, ignore_pos_arg_names: bool = False) -> bool: """Is 'left' subtype of 'right'? Also consider Any to be a subtype of any type, and vice versa. This @@ -45,10 +49,12 @@ def is_subtype(left: Type, right: Type, or isinstance(right, ErasedType)): return True elif isinstance(right, UnionType) and not isinstance(left, UnionType): - return any(is_subtype(left, item, type_parameter_checker) + return any(is_subtype(left, item, type_parameter_checker, + ignore_pos_arg_names=ignore_pos_arg_names) for item in right.items) else: - return left.accept(SubtypeVisitor(right, type_parameter_checker)) + return left.accept(SubtypeVisitor(right, type_parameter_checker, + ignore_pos_arg_names=ignore_pos_arg_names)) def is_subtype_ignoring_tvars(left: Type, right: Type) -> bool: @@ -58,8 +64,11 @@ def ignore_tvars(s: Type, t: Type, v: int) -> bool: def is_equivalent(a: Type, b: Type, - type_parameter_checker: TypeParameterChecker = check_type_parameter) -> bool: - return is_subtype(a, b, type_parameter_checker) and is_subtype(b, a, type_parameter_checker) + type_parameter_checker: TypeParameterChecker = check_type_parameter, + *, ignore_pos_arg_names=False) -> bool: + return ( + is_subtype(a, b, type_parameter_checker, ignore_pos_arg_names=ignore_pos_arg_names) + and is_subtype(b, a, type_parameter_checker, ignore_pos_arg_names=ignore_pos_arg_names)) def satisfies_upper_bound(a: Type, upper_bound: Type) -> bool: @@ -78,9 +87,11 @@ def h() -> None: ... class SubtypeVisitor(TypeVisitor[bool]): def __init__(self, right: Type, - type_parameter_checker: TypeParameterChecker) -> None: + type_parameter_checker: TypeParameterChecker, + *, ignore_pos_arg_names: bool = False) -> None: self.right = right self.check_type_parameter = type_parameter_checker + self.ignore_pos_arg_names = ignore_pos_arg_names # visit_x(left) means: is left (which is an instance of X) a subtype of # right? @@ -123,9 +134,9 @@ def visit_instance(self, left: Instance) -> bool: if isinstance(right, TupleType) and right.fallback.type.is_enum: return is_subtype(left, right.fallback) if isinstance(right, Instance): - if left.type._promote and is_subtype(left.type._promote, - self.right, - self.check_type_parameter): + if left.type._promote and is_subtype( + left.type._promote, self.right, self.check_type_parameter, + ignore_pos_arg_names=self.ignore_pos_arg_names): return True rname = right.type.fullname() if not left.type.has_base(rname) and rname != 'builtins.object': @@ -149,12 +160,16 @@ def visit_type_var(self, left: TypeVarType) -> bool: def visit_callable_type(self, left: CallableType) -> bool: right = self.right if isinstance(right, CallableType): - return is_callable_subtype(left, right) + return is_callable_subtype( + left, right, + ignore_pos_arg_names=self.ignore_pos_arg_names) elif isinstance(right, Overloaded): - return all(is_subtype(left, item, self.check_type_parameter) + return all(is_subtype(left, item, self.check_type_parameter, + ignore_pos_arg_names=self.ignore_pos_arg_names) for item in right.items()) elif isinstance(right, Instance): - return is_subtype(left.fallback, right) + return is_subtype(left.fallback, right, + ignore_pos_arg_names=self.ignore_pos_arg_names) elif isinstance(right, TypeType): # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and is_subtype(left.ret_type, right.item) @@ -210,7 +225,8 @@ def visit_overloaded(self, left: Overloaded) -> bool: 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): + 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): @@ -218,7 +234,8 @@ def visit_overloaded(self, left: Overloaded) -> bool: 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): + 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): @@ -255,9 +272,14 @@ def visit_type_type(self, left: TypeType) -> bool: def is_callable_subtype(left: CallableType, right: CallableType, - ignore_return: bool = False) -> bool: + ignore_return: bool = False, + ignore_pos_arg_names: bool = False) -> bool: """Is left a subtype of right?""" - # TODO: Support named arguments, **args, etc. + + # If either function is implicitly typed, ignore positional arg names too + if left.implicit or right.implicit: + ignore_pos_arg_names = True + # Non-type cannot be a subtype of type. if right.is_type_obj() and not left.is_type_obj(): return False @@ -286,45 +308,157 @@ def is_callable_subtype(left: CallableType, right: CallableType, if right.is_ellipsis_args: return True - # Check argument types. - if left.min_args > right.min_args: - return False - if left.is_var_arg: - return is_var_arg_callable_subtype_helper(left, right) - if right.is_var_arg: - return False - if len(left.arg_types) < len(right.arg_types): - return False - for i in range(len(right.arg_types)): - if not is_subtype(right.arg_types[i], left.arg_types[i]): - return False - return True + right_star_type = None # type: Optional[Type] + right_star2_type = None # type: Optional[Type] + # Match up corresponding arguments and check them for compatibility. In + # every pair (argL, argR) of corresponding arguments from L and R, argL must + # be "more general" than argR if L is to be a subtype of R. -def is_var_arg_callable_subtype_helper(left: CallableType, right: CallableType) -> bool: - """Is left a subtype of right, assuming left has *args? + # Arguments are corresponding if they either share a name, share a position, + # or both. If L's corresponding argument is ambiguous, L is not a subtype of + # R. - See also is_callable_subtype for additional assumptions we can make. - """ - left_fixed = left.max_fixed_args() - right_fixed = right.max_fixed_args() - num_fixed_matching = min(left_fixed, right_fixed) - for i in range(num_fixed_matching): - if not is_subtype(right.arg_types[i], left.arg_types[i]): - return False - if not right.is_var_arg: - for i in range(num_fixed_matching, len(right.arg_types)): - if not is_subtype(right.arg_types[i], left.arg_types[-1]): + # If left has one corresponding argument by name and another by position, + # consider them to be one "merged" argument (and not ambiguous) if they're + # both optional, they're name-only and position-only respectively, and they + # have the same type. This rule allows functions with (*args, **kwargs) to + # properly stand in for the full domain of formal arguments that they're + # used for in practice. + + # Every argument in R must have a corresponding argument in L, and every + # required argument in L must have a corresponding argument in R. + done_with_positional = False + for i in range(len(right.arg_types)): + right_kind = right.arg_kinds[i] + if right_kind in (ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT): + done_with_positional = True + right_required = right_kind in (ARG_POS, ARG_NAMED) + right_pos = None if done_with_positional else i + + right_arg = FormalArgument( + right.arg_names[i], + right_pos, + right.arg_types[i], + right_required) + + if right_kind == ARG_STAR: + right_star_type = right_arg.typ + # Right has an infinite series of optional positional arguments + # here. Get all further positional arguments of left, and make sure + # they're more general than their corresponding member in this + # series. Also make sure left has its own inifite series of + # optional positional arguments. + if not left.is_var_arg: return False - return True - else: - for i in range(left_fixed, right_fixed): - if not is_subtype(right.arg_types[i], left.arg_types[-1]): + j = i + while j < len(left.arg_kinds) and left.arg_kinds[j] in (ARG_POS, ARG_OPT): + left_by_position = left.argument_by_position(j) + assert left_by_position is not None + # This fetches the synthetic argument that's from the *args + right_by_position = right.argument_by_position(j) + assert right_by_position is not None + if not are_args_compatible(left_by_position, right_by_position, + ignore_pos_arg_names): + return False + j += 1 + continue + + if right_kind == ARG_STAR2: + right_star2_type = right_arg.typ + # Right has an infinite set of optional named arguments here. Get + # all further named arguments of left and make sure they're more + # general than their corresponding member in this set. Also make + # sure left has its own infinite set of optional named arguments. + if not left.is_kw_arg: + return False + left_names = {name for name in left.arg_names if name is not None} + right_names = {name for name in right.arg_names if name is not None} + left_only_names = left_names - right_names + for name in left_only_names: + left_by_name = left.argument_by_name(name) + assert left_by_name is not None + # This fetches the synthetic argument that's from the **kwargs + right_by_name = right.argument_by_name(name) + assert right_by_name is not None + if not are_args_compatible(left_by_name, right_by_name, + ignore_pos_arg_names): + return False + continue + + # Left must have some kind of corresponding argument. + left_arg = left.corresponding_argument(right_arg) + if left_arg is None: + return False + + if not are_args_compatible(left_arg, right_arg, ignore_pos_arg_names): + return False + + done_with_positional = False + for i in range(len(left.arg_types)): + left_kind = left.arg_kinds[i] + if left_kind in (ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT): + done_with_positional = True + left_arg = FormalArgument( + left.arg_names[i], + None if done_with_positional else i, + left.arg_types[i], + left_kind in (ARG_POS, ARG_NAMED)) + + # Check that *args and **kwargs types match in this loop + if left_kind == ARG_STAR: + if right_star_type is not None and not is_subtype(right_star_type, left_arg.typ): return False - for i in range(right_fixed, left_fixed): - if not is_subtype(right.arg_types[-1], left.arg_types[i]): + continue + elif left_kind == ARG_STAR2: + if right_star2_type is not None and not is_subtype(right_star2_type, left_arg.typ): return False - return is_subtype(right.arg_types[-1], left.arg_types[-1]) + continue + + right_by_name = (right.argument_by_name(left_arg.name) + if left_arg.name is not None + else None) + + right_by_pos = (right.argument_by_position(left_arg.pos) + if left_arg.pos is not None + else None) + + # If the left hand argument corresponds to two right-hand arguments, + # neither of them can be required. + if (right_by_name is not None + and right_by_pos is not None + and right_by_name != right_by_pos + and (right_by_pos.required or right_by_name.required)): + return False + + # All *required* left-hand arguments must have a corresponding + # right-hand argument. Optional args it does not matter. + if left_arg.required and right_by_pos is None and right_by_name is None: + return False + + return True + + +def are_args_compatible( + left: FormalArgument, + right: FormalArgument, + ignore_pos_arg_names: bool) -> bool: + # If right has a specific name it wants this argument to be, left must + # have the same. + if right.name is not None and left.name != right.name: + # But pay attention to whether we're ignoring positional arg names + if not ignore_pos_arg_names or right.pos is None: + return False + # If right is at a specific position, left must have the same: + if right.pos is not None and left.pos != right.pos: + return False + # Left must have a more general type + if not is_subtype(right.typ, left.typ): + return False + # If right's argument is optional, left's must also be. + if not right.required and left.required: + return False + return True def unify_generic_callable(type: CallableType, target: CallableType, diff --git a/mypy/types.py b/mypy/types.py index 57936bed10fb..e852f96e5730 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -5,6 +5,7 @@ from collections import OrderedDict from typing import ( Any, TypeVar, Dict, List, Tuple, cast, Generic, Set, Sequence, Optional, Union, Iterable, + NamedTuple, ) import mypy.nodes @@ -14,6 +15,7 @@ ) from mypy import experiments +from mypy.sharedparse import argument_elide_name T = TypeVar('T') @@ -542,6 +544,13 @@ def deserialize(cls, data: JsonDict) -> 'FunctionLike': _dummy = object() # type: Any +FormalArgument = NamedTuple('FormalArgument', [ + ('name', Optional[str]), + ('pos', Optional[int]), + ('typ', Type), + ('required', bool)]) + + class CallableType(FunctionLike): """Type of a non-overloaded callable object (function).""" @@ -662,6 +671,78 @@ def max_fixed_args(self) -> int: n -= 1 return n + def corresponding_argument(self, model: FormalArgument) -> Optional[FormalArgument]: + """Return the argument in this function that corresponds to `model`""" + + by_name = self.argument_by_name(model.name) + by_pos = self.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 argument_by_name(self, name: str) -> Optional[FormalArgument]: + if name is None: + return None + seen_star = False + star2_type = None # type: Optional[Type] + for i, (arg_name, kind, typ) in enumerate( + zip(self.arg_names, self.arg_kinds, self.arg_types)): + # No more positional arguments after these. + if kind in (ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT): + seen_star = True + if kind == ARG_STAR: + continue + if kind == ARG_STAR2: + star2_type = typ + continue + if arg_name == name: + position = None if seen_star else i + return FormalArgument(name, position, typ, kind in (ARG_POS, ARG_NAMED)) + if star2_type is not None: + return FormalArgument(name, None, star2_type, False) + return None + + def argument_by_position(self, position: int) -> Optional[FormalArgument]: + if position is None: + return None + if self.is_var_arg: + for kind, typ in zip(self.arg_kinds, self.arg_types): + if kind == ARG_STAR: + star_type = typ + break + if position >= len(self.arg_names): + if self.is_var_arg: + return FormalArgument(None, position, star_type, False) + else: + return None + name, kind, typ = ( + self.arg_names[position], + self.arg_kinds[position], + self.arg_types[position], + ) + if kind in (ARG_POS, ARG_OPT): + return FormalArgument(name, position, typ, kind == ARG_POS) + else: + if self.is_var_arg: + return FormalArgument(None, position, star_type, False) + else: + return None + def items(self) -> List['CallableType']: return [self] @@ -1647,7 +1728,7 @@ def function_type(func: mypy.nodes.FuncBase, fallback: Instance) -> FunctionLike return CallableType( [AnyType()] * len(fdef.arg_names), fdef.arg_kinds, - fdef.arg_names, + [None if argument_elide_name(n) else n for n in fdef.arg_names], AnyType(), fallback, name, diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 0d0dcf74c511..31a61688eeee 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -21,6 +21,41 @@ b = f(a) class A: pass class B: pass +[case testKeywordOnlyArgumentOrderInsensitivity] +import typing + +class A(object): + def f(self, *, a: int, b: str) -> None: pass + +class B(A): + def f(self, *, b: str, a: int) -> None: pass + +class C(A): + def f(self, *, b: int, a: str) -> None: pass # E: Signature of "f" incompatible with supertype "A" + +[case testPositionalOverridingArgumentNameInsensitivity] +import typing + +class A(object): + def f(self, a: int, b: str) -> None: pass + +class B(A): + def f(self, b: str, a: int) -> None: pass # E: Argument 1 of "f" incompatible with supertype "A" # E: Argument 2 of "f" incompatible with supertype "A" + +class C(A): + def f(self, foo: int, bar: str) -> None: pass + + +[case testPositionalOverridingArgumentNamesCheckedWhenMismatchingPos] +import typing + +class A(object): + def f(self, a: int, b: str) -> None: pass + +class B(A): + def f(self, b: int, a: str) -> None: pass # E: Signature of "f" incompatible with supertype "A" + + [case testSubtypingFunctionTypes] from typing import Callable @@ -40,6 +75,100 @@ f = f g = g h = h +[case testSubtypingFunctionsDoubleCorrespondence] + +def l(x) -> None: ... +def r(__, *, x) -> None: ... +r = l # E: Incompatible types in assignment (expression has type Callable[[Any], None], variable has type Callable[[Any, NamedArg('x', Any)], None]) + +[case testSubtypingFunctionsRequiredLeftArgNotPresent] + +def l(x, y) -> None: ... +def r(x) -> None: ... +r = l # E: Incompatible types in assignment (expression has type Callable[[Any, Any], None], variable has type Callable[[Any], None]) + +[case testSubtypingFunctionsImplicitNames] + +def f(a, b): pass +def g(c: Any, d: Any) -> Any: pass + +ff = f +gg = g + +gg = f +ff = g + +[case testSubtypingFunctionsDefaultsNames] +from typing import Callable + +def f(a: int, b: str) -> None: pass +f_nonames = None # type: Callable[[int, str], None] +def g(a: int, b: str = "") -> None: pass +def h(aa: int, b: str = "") -> None: pass + +ff_nonames = f_nonames +ff = f +gg = g +hh = h + +ff = gg +ff_nonames = ff +ff_nonames = f_nonames # reset +ff = ff_nonames # E: Incompatible types in assignment (expression has type Callable[[int, str], None], variable has type Callable[[Arg('a', int), Arg('b', str)], None]) +ff = f # reset +gg = ff # E: Incompatible types in assignment (expression has type Callable[[Arg('a', int), Arg('b', str)], None], variable has type Callable[[Arg('a', int), DefaultArg('b', str)], None]) +gg = hh # E: Incompatible types in assignment (expression has type Callable[[Arg('aa', int), DefaultArg('b', str)], None], variable has type Callable[[Arg('a', int), DefaultArg('b', str)], None]) + +[case testSubtypingFunctionsArgsKwargs] +from typing import Any, Callable + +def everything(*args: Any, **kwargs: Any) -> None: pass +everywhere = None # type: Callable[..., None] + +def specific_1(a: int, b: str) -> None: pass +def specific_2(a: int, *, b: str) -> None: pass + +ss_1 = specific_1 +ss_2 = specific_2 +ee_def = everything +ee_var = everywhere + +ss_1 = ee_def +ss_1 = specific_1 +ss_2 = ee_def +ss_2 = specific_2 +ee_def = everywhere +ee_def = everything +ee_var = everything +ee_var = everywhere + +ee_var = specific_1 # The difference between Callable[..., blah] and one with a *args: Any, **kwargs: Any is that the ... goes loosely both ways. +ee_def = specific_1 # E: Incompatible types in assignment (expression has type Callable[[int, str], None], variable has type Callable[[StarArg(Any), KwArg(Any)], None]) + +[builtins fixtures/dict.pyi] + +[case testLackOfNames] +def f(__a: int, __b: str) -> None: pass +def g(a: int, b: str) -> None: pass + +ff = f +gg = g + +ff = g +gg = f # E: Incompatible types in assignment (expression has type Callable[[int, str], None], variable has type Callable[[Arg('a', int), Arg('b', str)], None]) + +[case testLackOfNamesFastparse] +# flags: --fast-parser + +def f(__a: int, __b: str) -> None: pass +def g(a: int, b: str) -> None: pass + +ff = f +gg = g + +ff = g +gg = f # E: Incompatible types in assignment (expression has type Callable[[int, str], None], variable has type Callable[[Arg('a', int), Arg('b', str)], None]) + [case testFunctionTypeCompatibilityWithOtherTypes] from typing import Callable f = None # type: Callable[[], None] @@ -1276,7 +1405,7 @@ def g4(*, y: int) -> str: pass f(g1) f(g2) f(g3) -f(g4) # E: Argument 1 to "f" has incompatible type Callable[[int], str]; expected Callable[..., int] +f(g4) # E: Argument 1 to "f" has incompatible type Callable[[NamedArg('y', int)], str]; expected Callable[..., int] [case testCallableWithArbitraryArgsSubtypingWithGenericFunc] from typing import Callable, TypeVar @@ -1399,10 +1528,12 @@ class A(Generic[t]): [case testRedefineFunction] -def f(x): pass +def f(x) -> Any: pass def g(x, y): pass -def h(y): pass +def h(x): pass +def j(y) -> Any: pass f = h +f = j # E: Incompatible types in assignment (expression has type Callable[[Arg('y', Any)], Any], variable has type Callable[[Arg('x', Any)], Any]) f = g # E: Incompatible types in assignment (expression has type Callable[[Any, Any], Any], variable has type Callable[[Any], Any]) [case testRedefineFunction2] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index d567763b3684..fec75b689c19 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -594,7 +594,7 @@ try: except: f = g [file m.py] -def f(y): pass +def f(x): pass [case testImportFunctionAndAssignIncompatible] try: diff --git a/test-data/unit/check-super.test b/test-data/unit/check-super.test index da7ca9060081..2993113f5426 100644 --- a/test-data/unit/check-super.test +++ b/test-data/unit/check-super.test @@ -22,7 +22,7 @@ from typing import Any class B: def f(self, y: 'A') -> None: pass class A(B): - def f(self, x: Any) -> None: + def f(self, y: Any) -> None: a, b = None, None # type: (A, B) super().f(b) # E: Argument 1 to "f" of "B" has incompatible type "B"; expected "A" super().f(a) diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index e59a332b00f6..89120bffe153 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -572,7 +572,7 @@ x = None # type: Callable[[int], None] def f(*x: int) -> None: pass def g(*x: str) -> None: pass x = f -x = g # E: Incompatible types in assignment (expression has type Callable[[str], None], variable has type Callable[[int], None]) +x = g # E: Incompatible types in assignment (expression has type Callable[[StarArg(str)], None], variable has type Callable[[int], None]) [builtins fixtures/list.pyi] [out] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 6bf0e97925ae..1887177d3320 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -755,7 +755,7 @@ def f(*args: str) -> str: return args[0] map(f, ['x']) map(f, [1]) [out] -_program.py:4: error: Argument 1 to "map" has incompatible type Callable[[str], str]; expected Callable[[int], str] +_program.py:4: error: Argument 1 to "map" has incompatible type Callable[[StarArg(str)], str]; expected Callable[[int], str] [case testMapStr] import typing