Skip to content

Commit e0b4b05

Browse files
authored
Now lambda inherits typed context in semanal.py (#11213)
Closes #11155
1 parent 8e82171 commit e0b4b05

File tree

4 files changed

+129
-4
lines changed

4 files changed

+129
-4
lines changed

mypy/semanal.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4961,9 +4961,26 @@ def in_checked_function(self) -> bool:
49614961
- Yes in annotated functions.
49624962
- No otherwise.
49634963
"""
4964-
return (self.options.check_untyped_defs
4965-
or not self.function_stack
4966-
or not self.function_stack[-1].is_dynamic())
4964+
if self.options.check_untyped_defs or not self.function_stack:
4965+
return True
4966+
4967+
current_index = len(self.function_stack) - 1
4968+
while current_index >= 0:
4969+
current_func = self.function_stack[current_index]
4970+
if (
4971+
isinstance(current_func, FuncItem)
4972+
and not isinstance(current_func, LambdaExpr)
4973+
):
4974+
return not current_func.is_dynamic()
4975+
4976+
# Special case, `lambda` inherits the "checked" state from its parent.
4977+
# Because `lambda` itself cannot be annotated.
4978+
# `lambdas` can be deeply nested, so we try to find at least one other parent.
4979+
current_index -= 1
4980+
4981+
# This means that we only have a stack of `lambda` functions,
4982+
# no regular functions.
4983+
return True
49674984

49684985
def fail(self,
49694986
msg: str,

mypy/test/testsemanal.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
'semanal-typeddict.test',
3333
'semenal-literal.test',
3434
'semanal-classvar.test',
35-
'semanal-python2.test']
35+
'semanal-python2.test',
36+
'semanal-lambda.test']
3637

3738

3839
def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Options:

test-data/unit/check-expressions.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2366,6 +2366,19 @@ def f() -> None:
23662366
[out]
23672367
main:3: note: Revealed type is "builtins.int"
23682368

2369+
[case testLambdaTypedContext]
2370+
def f() -> None:
2371+
lambda: 'a'.missing() # E: "str" has no attribute "missing"
2372+
2373+
[case testLambdaUnypedContext]
2374+
def f():
2375+
lambda: 'a'.missing()
2376+
2377+
[case testLambdaCheckUnypedContext]
2378+
# flags: --check-untyped-defs
2379+
def f():
2380+
lambda: 'a'.missing() # E: "str" has no attribute "missing"
2381+
23692382
[case testEqNone]
23702383
None == None
23712384
[builtins fixtures/ops.pyi]

test-data/unit/semanal-lambda.test

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
[case testLambdaInheritsCheckedContextFromFunc]
2+
def g():
3+
return lambda x: UNDEFINED in x
4+
[out]
5+
MypyFile:1(
6+
FuncDef:1(
7+
g
8+
Block:1(
9+
ReturnStmt:2(
10+
LambdaExpr:2(
11+
Args(
12+
Var(x))
13+
Block:2(
14+
ReturnStmt:2(
15+
ComparisonExpr:2(
16+
in
17+
NameExpr(UNDEFINED)
18+
NameExpr(x [l])))))))))
19+
20+
[case testLambdaInheritsCheckedContextFromFuncForced]
21+
# flags: --check-untyped-defs
22+
def g():
23+
return lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
24+
25+
[case testLambdaInheritsCheckedContextFromTypedFunc]
26+
def g() -> None:
27+
return lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
28+
29+
[case testLambdaInheritsCheckedContextFromTypedFuncForced]
30+
# flags: --check-untyped-defs
31+
def g() -> None:
32+
return lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
33+
34+
[case testLambdaInheritsCheckedContextFromModule]
35+
g = lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
36+
37+
[case testLambdaInheritsCheckedContextFromModuleForce]
38+
# flags: --check-untyped-defs
39+
g = lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
40+
41+
[case testLambdaInheritsCheckedContextFromModuleLambdaStack]
42+
g = lambda: lambda: lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
43+
44+
[case testLambdaInheritsCheckedContextFromModuleLambdaStackForce]
45+
# flags: --check-untyped-defs
46+
g = lambda: lambda: lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
47+
48+
[case testLambdaInheritsCheckedContextFromFuncLambdaStack]
49+
def g():
50+
return lambda: lambda: lambda x: UNDEFINED in x
51+
[out]
52+
MypyFile:1(
53+
FuncDef:1(
54+
g
55+
Block:1(
56+
ReturnStmt:2(
57+
LambdaExpr:2(
58+
Block:2(
59+
ReturnStmt:2(
60+
LambdaExpr:2(
61+
Block:2(
62+
ReturnStmt:2(
63+
LambdaExpr:2(
64+
Args(
65+
Var(x))
66+
Block:2(
67+
ReturnStmt:2(
68+
ComparisonExpr:2(
69+
in
70+
NameExpr(UNDEFINED)
71+
NameExpr(x [l])))))))))))))))
72+
73+
[case testLambdaInheritsCheckedContextFromFuncLambdaStackForce]
74+
# flags: --check-untyped-defs
75+
def g():
76+
return lambda: lambda: lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
77+
78+
[case testLambdaInheritsCheckedContextFromTypedFuncLambdaStack]
79+
def g() -> None:
80+
return lambda: lambda: lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
81+
82+
[case testLambdaInheritsCheckedContextFromTypedFuncLambdaStackForce]
83+
# flags: --check-untyped-defs
84+
def g() -> None:
85+
return lambda: lambda: lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
86+
87+
[case testLambdaInheritsCheckedContextFromClassLambdaStack]
88+
class A:
89+
g = lambda: lambda: lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined
90+
91+
[case testLambdaInheritsCheckedContextFromClassLambdaStackForce]
92+
# flags: --check-untyped-defs
93+
class A:
94+
g = lambda: lambda: lambda x: UNDEFINED in x # E: Name "UNDEFINED" is not defined

0 commit comments

Comments
 (0)