Skip to content

Commit 8992bb8

Browse files
committed
Merge branch 'with-lvalue'
2 parents f4aca38 + c8cbe43 commit 8992bb8

11 files changed

+58
-29
lines changed

mypy/checker.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,12 +1638,12 @@ def visit_decorator(self, e: Decorator) -> Type:
16381638

16391639
def visit_with_stmt(self, s: WithStmt) -> Type:
16401640
echk = self.expr_checker
1641-
for expr, name in zip(s.expr, s.name):
1641+
for expr, target in zip(s.expr, s.target):
16421642
ctx = self.accept(expr)
16431643
enter = echk.analyse_external_member_access('__enter__', ctx, expr)
16441644
obj = echk.check_call(enter, [], [], expr)[0]
1645-
if name:
1646-
self.check_assignment(name, self.temp_node(obj, expr))
1645+
if target:
1646+
self.check_assignment(target, self.temp_node(obj, expr))
16471647
exit = echk.analyse_external_member_access('__exit__', ctx, expr)
16481648
arg = self.temp_node(AnyType(), expr)
16491649
echk.check_call(exit, [arg] * 3, [nodes.ARG_POS] * 3, expr)

mypy/nodes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,13 +701,13 @@ def accept(self, visitor: NodeVisitor[T]) -> T:
701701

702702
class WithStmt(Node):
703703
expr = Undefined(List[Node])
704-
name = Undefined(List['NameExpr'])
704+
target = Undefined(List[Node])
705705
body = Undefined(Block)
706706

707-
def __init__(self, expr: List[Node], name: List['NameExpr'],
707+
def __init__(self, expr: List[Node], target: List[Node],
708708
body: Block) -> None:
709709
self.expr = expr
710-
self.name = name
710+
self.target = target
711711
self.body = body
712712

713713
def accept(self, visitor: NodeVisitor[T]) -> T:

mypy/parse.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -967,23 +967,22 @@ def parse_try_stmt(self) -> Node:
967967

968968
def parse_with_stmt(self) -> WithStmt:
969969
self.expect('with')
970-
expr = List[Node]()
971-
name = List[NameExpr]()
970+
exprs = List[Node]()
971+
targets = List[Node]()
972972
while True:
973-
e = self.parse_expression(precedence[','])
973+
expr = self.parse_expression(precedence[','])
974974
if self.current_str() == 'as':
975975
self.expect('as')
976-
n = self.parse_name_expr()
976+
target = self.parse_expression(precedence[','])
977977
else:
978-
n = None
979-
expr.append(e)
980-
name.append(n)
978+
target = None
979+
exprs.append(expr)
980+
targets.append(target)
981981
if self.current_str() != ',':
982982
break
983983
self.expect(',')
984984
body, _ = self.parse_block()
985-
node = WithStmt(expr, name, body)
986-
return node
985+
return WithStmt(exprs, targets, body)
987986

988987
def parse_print_stmt(self) -> PrintStmt:
989988
self.expect('print')

mypy/semanal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,7 @@ def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor,
13101310
def visit_with_stmt(self, s: WithStmt) -> None:
13111311
for e in s.expr:
13121312
e.accept(self)
1313-
for n in s.name:
1313+
for n in s.target:
13141314
if n:
13151315
self.analyse_lvalue(n)
13161316
self.visit_block(s.body)
@@ -1876,7 +1876,7 @@ def visit_for_stmt(self, s: ForStmt) -> None:
18761876
self.sem.analyse_lvalue(s.index, add_global=True)
18771877

18781878
def visit_with_stmt(self, s: WithStmt) -> None:
1879-
for n in s.name:
1879+
for n in s.target:
18801880
if n:
18811881
self.sem.analyse_lvalue(n, add_global=True)
18821882

mypy/strconv.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ def visit_with_stmt(self, o):
266266
a = []
267267
for i in range(len(o.expr)):
268268
a.append(('Expr', [o.expr[i]]))
269-
if o.name[i]:
270-
a.append(('Name', [o.name[i]]))
269+
if o.target[i]:
270+
a.append(('Target', [o.target[i]]))
271271
return self.dump(a + [o.body], o)
272272

273273
def visit_print_stmt(self, o):

mypy/test/data/check-statements.test

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,17 @@ with A[B]() as b, A[C]() as c:
804804
b = c # E: Incompatible types in assignment (expression has type "C", variable has type "B")
805805
c = b # E: Incompatible types in assignment (expression has type "B", variable has type "C")
806806

807+
[case testWithStmtAndComplexTarget]
808+
from typing import Tuple
809+
class A:
810+
def __enter__(self) -> Tuple[int, str]: pass
811+
def __exit__(self, x, y, z): pass
812+
with A() as (a, b):
813+
a = 1
814+
b = ''
815+
a = b # E: Incompatible types in assignment (expression has type "str", variable has type "int")
816+
[builtins fixtures/tuple.py]
817+
807818

808819
-- Chained assignment
809820
-- ------------------

mypy/test/data/parse.test

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,7 @@ MypyFile:1(
16301630
NameExpr(open)
16311631
Args(
16321632
StrExpr(foo))))
1633-
Name(
1633+
Target(
16341634
NameExpr(f))
16351635
Block:1(
16361636
PassStmt:2())))
@@ -2154,11 +2154,11 @@ MypyFile:1(
21542154
WithStmt:1(
21552155
Expr(
21562156
NameExpr(x))
2157-
Name(
2157+
Target(
21582158
NameExpr(y))
21592159
Expr(
21602160
NameExpr(a))
2161-
Name(
2161+
Target(
21622162
NameExpr(b))
21632163
Block:1(
21642164
PassStmt:2()))
@@ -3117,3 +3117,17 @@ MypyFile:1(
31173117
IntExpr(2))
31183118
NameExpr(x)
31193119
NameExpr(y)))))
3120+
3121+
[case testComplexWithLvalue]
3122+
with x as y.z: pass
3123+
[out]
3124+
MypyFile:1(
3125+
WithStmt:1(
3126+
Expr(
3127+
NameExpr(x))
3128+
Target(
3129+
MemberExpr:1(
3130+
NameExpr(y)
3131+
z))
3132+
Block:1(
3133+
PassStmt:1())))

mypy/test/data/semanal-errors.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,3 +1306,8 @@ x = Undefined(Function[[], None])
13061306
[out]
13071307
main, line 2: Name 'Function' is not defined
13081308
main, line 2: (Did you mean 'typing.Callable'?)
1309+
1310+
[case testInvalidWithTarget]
1311+
def f(): pass
1312+
with f() as 1: pass # E: Invalid assignment target
1313+
[out]

mypy/test/data/semanal-statements.test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ MypyFile:1(
630630
WithStmt:1(
631631
Expr(
632632
NameExpr(object [builtins.object]))
633-
Name(
633+
Target(
634634
NameExpr(x* [__main__.x]))
635635
Block:1(
636636
ExpressionStmt:2(
@@ -648,7 +648,7 @@ MypyFile:1(
648648
WithStmt:2(
649649
Expr(
650650
NameExpr(f [__main__.f]))
651-
Name(
651+
Target(
652652
NameExpr(x* [l]))
653653
Block:2(
654654
ExpressionStmt:3(
@@ -671,11 +671,11 @@ MypyFile:1(
671671
WithStmt:3(
672672
Expr(
673673
NameExpr(object [builtins.object]))
674-
Name(
674+
Target(
675675
NameExpr(a* [__main__.a]))
676676
Expr(
677677
NameExpr(object [builtins.object]))
678-
Name(
678+
Target(
679679
NameExpr(b* [__main__.b]))
680680
Block:3(
681681
PassStmt:4())))

mypy/traverser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ def visit_try_stmt(self, o: TryStmt) -> T:
139139
def visit_with_stmt(self, o: WithStmt) -> T:
140140
for i in range(len(o.expr)):
141141
o.expr[i].accept(self)
142-
if o.name[i] is not None:
143-
o.name[i].accept(self)
142+
if o.target[i] is not None:
143+
o.target[i].accept(self)
144144
o.body.accept(self)
145145

146146
def visit_member_expr(self, o: MemberExpr) -> T:

mypy/treetransform.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ def visit_try_stmt(self, node: TryStmt) -> Node:
259259

260260
def visit_with_stmt(self, node: WithStmt) -> Node:
261261
return WithStmt(self.nodes(node.expr),
262-
self.optional_names(node.name),
262+
self.optional_nodes(node.target),
263263
self.block(node.body))
264264

265265
def visit_print_stmt(self, node: PrintStmt) -> Node:

0 commit comments

Comments
 (0)