Skip to content

Commit 38b9ad5

Browse files
committed
Improve handling of "lambda: None"
"lambda: None" might be used either where a function returning an Optional[T] is expected, or where a function not returning a value is expected. Previously it would always treated as the former; now the context is used to decide. When no context is available, the lambda is now treated as a function not returning a value. This seems more consistent with the fact that you can't define a variable 'a = None' without a type annotation or declare a function to return NoneTyp. (If really desperate for the other meaning of "lambda: None", you can write a generic function.) Fixes python#1425.
1 parent 9e4cfc8 commit 38b9ad5

File tree

3 files changed

+23
-0
lines changed

3 files changed

+23
-0
lines changed

mypy/checkexpr.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,9 @@ def visit_func_expr(self, e: FuncExpr) -> Type:
12621262
if not inferred_type:
12631263
# No useful type context.
12641264
ret_type = e.expr().accept(self.chk)
1265+
if isinstance(ret_type, NoneTyp):
1266+
# Refuse to infer NoneTyp as return type. Use Void instead.
1267+
ret_type = Void()
12651268
if not e.arguments:
12661269
# Form 'lambda: e'; just use the inferred return type.
12671270
return CallableType([], [], [], ret_type, self.named_type('builtins.function'))
@@ -1276,6 +1279,11 @@ def visit_func_expr(self, e: FuncExpr) -> Type:
12761279
if e.expr() not in self.chk.type_map:
12771280
self.accept(e.expr())
12781281
ret_type = self.chk.type_map[e.expr()]
1282+
if isinstance(ret_type, NoneTyp):
1283+
# For "lambda ...: None", just use type from the context.
1284+
# Important when the context is Callable[..., None] which
1285+
# really means Void. See #1425.
1286+
return inferred_type
12791287
return replace_callable_return_type(inferred_type, ret_type)
12801288

12811289
def infer_lambda_type_using_context(self, e: FuncExpr) -> CallableType:

mypy/test/data/check-inference-context.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,13 @@ T = TypeVar('T')
611611
def foo(arg: Callable[..., T]) -> None: pass
612612
foo(lambda: 1)
613613

614+
[case testLambdaNoneInContext]
615+
from typing import Callable
616+
def f(x: Callable[[], None]) -> None: pass
617+
def g(x: Callable[[], int]) -> None: pass
618+
f(lambda: None)
619+
g(lambda: None)
620+
614621

615622
-- Overloads + generic functions
616623
-- -----------------------------

mypy/test/data/check-inference.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,14 @@ class A: pass
10261026
class B: pass
10271027
[builtins fixtures/list.py]
10281028

1029+
[case testInferLambdaNone]
1030+
from typing import Callable
1031+
def f(x: Callable[[], None]) -> None: pass
1032+
def g(x: Callable[[], int]) -> None: pass
1033+
a = lambda: None
1034+
f(a)
1035+
g(a) # E: Argument 1 to "g" has incompatible type Callable[[], None]; expected Callable[[], int]
1036+
10291037

10301038
-- Boolean operators
10311039
-- -----------------

0 commit comments

Comments
 (0)