Repository: python/mypy

Issue:
Incompatible types in assignment with --strict-optional for a: Callable[..., None] = lambda *a, **k: None
This is a residue of https://github.com/python/mypy/pull/2634#issuecomment-271392350

This snippet causes an error when using `--strict-optional`:
```python
def f(a: Callable[..., None] = lambda *a, **k: None): pass
```
Error:
```
__tmp__.py:7: error: Incompatible types in assignment (expression has type Callable[[StarArg(Any), KwArg(Any)], None], variable has type Callable[..., None])
```
However this snippet is fine -- the only difference being that the return type is not `None`:
```python
def f(a: Callable[..., int] = lambda *a, **k: 0): pass
```
So @elazarg's claim "[it's not about the the parameters but about the return type](https://github.com/python/mypy/pull/2634#issuecomment-271436422)" is incorrect here.

And yes, this did break one place in our codebase. As a workaround I am putting `cast(Callable, ...)` around the `lambda` expression:
```python
def f(a: Callable[..., None] = cast(Callable, lambda *a, **k: None)): pass
```


Discussion:
 I am confused. This *is* about the return type, isn't it? 

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -614,7 +614,7 @@ def is_implicit_any(t: Type) -> bool:
                                       .format(erased, ref_type), defn)
                     elif isinstance(arg_type, TypeVarType):
                         # Refuse covariant parameter type variables
-                        # TODO: check recuresively for inner type variables
+                        # TODO: check recursively for inner type variables
                         if arg_type.variance == COVARIANT:
                             self.fail(messages.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT, arg_type)
                     if typ.arg_kinds[i] == nodes.ARG_STAR:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1742,7 +1742,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
 
     def visit_func_expr(self, e: FuncExpr) -> Type:
         """Type check lambda expression."""
-        inferred_type = self.infer_lambda_type_using_context(e)
+        inferred_type, type_override = self.infer_lambda_type_using_context(e)
         if not inferred_type:
             # No useful type context.
             ret_type = self.accept(e.expr())
@@ -1752,7 +1752,7 @@ def visit_func_expr(self, e: FuncExpr) -> Type:
             return callable_type(e, fallback, ret_type)
         else:
             # Type context available.
-            self.chk.check_func_item(e, type_override=inferred_type)
+            self.chk.check_func_item(e, type_override=type_override)
             if e.expr() not in self.chk.type_map:
                 self.accept(e.expr())
             ret_type = self.chk.type_map[e.expr()]
@@ -1763,10 +1763,12 @@ def visit_func_expr(self, e: FuncExpr) -> Type:
                 return inferred_type
             return replace_callable_return_type(inferred_type, ret_type)
 
-    def infer_lambda_type_using_context(self, e: FuncExpr) -> Optional[CallableType]:
+    def infer_lambda_type_using_context(self, e: FuncExpr) -> Tuple[Optional[CallableType],
+                                                                    Optional[CallableType]]:
         """Try to infer lambda expression type using context.
 
         Return None if could not infer type.
+        The second item in the return type is the type_override parameter for check_func_item.
         """
         # TODO also accept 'Any' context
         ctx = self.chk.type_context[-1]
@@ -1777,7 +1779,7 @@ def infer_lambda_type_using_context(self, e: FuncExpr) -> Optional[CallableType]
                 ctx = callables[0]
 
         if not ctx or not isinstance(ctx, CallableType):
-            return None
+            return None, None
 
         # The context may have function type variables in it. We replace them
         # since these are the type variables we are ultimately trying to infer;
@@ -1799,13 +1801,13 @@ def infer_lambda_type_using_context(self, e: FuncExpr) -> Optional[CallableType]
 
         if ARG_STAR in arg_kinds or ARG_STAR2 in arg_kinds:
             # TODO treat this case appropriately
-            return None
+            return callable_ctx, None
         if callable_ctx.arg_kinds != arg_kinds:
             # Incompatible context; cannot use it to infer types.
             self.chk.fail(messages.CANNOT_INFER_LAMBDA_TYPE, e)
-            return None
+            return None, None
 
-        return callable_ctx
+        return callable_ctx, callable_ctx
 
     def visit_super_expr(self, e: SuperExpr) -> Type:
         """Type check a super expression (non-lvalue)."""


Test patch:
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -1033,6 +1033,22 @@ b = lambda: None  # type: Callable[[], None]
 f(b)
 g(b) # E: Argument 1 to "g" has incompatible type Callable[[], None]; expected Callable[[], int]
 
+[case testLambdaDefaultContext]
+# flags: --strict-optional
+from typing import Callable
+def f(a: Callable[..., None] = lambda *a, **k: None):
+    pass
+
+def g(a: Callable[..., None] = lambda *a, **k: 1):  # E: Incompatible types in assignment (expression has type Callable[[StarArg(Any), KwArg(Any)], int], variable has type Callable[..., None])
+    pass
+[builtins fixtures/dict.pyi]
+
+[case testLambdaVarargContext]
+# Should not crash
+from typing import Callable
+def f(a: Callable[[int, int, int], int] = lambda *a, **k: 1):
+    pass
+[builtins fixtures/dict.pyi]
 
 -- Boolean operators
 -- -----------------