Skip to content

Commit eda36ec

Browse files
committed
Support 'with' statements with names (close #123)
1 parent 5de5a21 commit eda36ec

File tree

6 files changed

+53
-12
lines changed

6 files changed

+53
-12
lines changed

mypy/checker.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,8 @@ def is_definition(self, s):
769769
ctx = self.accept(expr)
770770
enter = echk.analyse_external_member_access('__enter__', ctx, expr)
771771
obj = echk.check_call(enter, [], [], expr)[0]
772+
if name:
773+
self.check_assignments([name], self.temp_node(obj, expr))
772774
exit = echk.analyse_external_member_access('__exit__', ctx, expr)
773775
arg = self.temp_node(Any(), expr)
774776
echk.check_call(exit, [arg] * 3, [nodes.ARG_POS] * 3, expr)

mypy/nodes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,10 @@ class TryStmt(Node):
581581

582582
class WithStmt(Node):
583583
Node[] expr
584-
Var[] name
584+
NameExpr[] name
585585
Block body
586586

587-
void __init__(self, Node[] expr, Var[] name, Block body):
587+
void __init__(self, Node[] expr, NameExpr[] name, Block body):
588588
self.expr = expr
589589
self.name = name
590590
self.body = body

mypy/parse.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -974,17 +974,17 @@ def parse_type_def(self, is_interface):
974974
Token[] as_toks = []
975975
Token[] commas = []
976976
Node[] expr = []
977-
Var[] name = []
977+
NameExpr[] name = []
978978
while True:
979979
e = self.parse_expression(precedence[','])
980-
Var v = None
981980
if self.current_str() == 'as':
982981
as_toks.append(self.expect('as'))
983-
v = self.parse_var()
982+
n = self.parse_name_expr()
984983
else:
985984
as_toks.append(none)
985+
n = None
986986
expr.append(e)
987-
name.append(v)
987+
name.append(n)
988988
if self.current_str() != ',':
989989
break
990990
commas.append(self.expect(','))

mypy/semanal.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ class SemanticAnalyzer(NodeVisitor):
106106
self.anal_var_def((VarDef)d)
107107
elif isinstance(d, ForStmt):
108108
self.anal_for_stmt((ForStmt)d)
109+
elif isinstance(d, WithStmt):
110+
self.anal_with_stmt((WithStmt)d)
109111
elif isinstance(d, Decorator):
110112
self.anal_decorator((Decorator)d)
111113
# Add implicit definition of 'None' to builtins, as we cannot define a
@@ -147,6 +149,11 @@ class SemanticAnalyzer(NodeVisitor):
147149
for n in s.index:
148150
self.analyse_lvalue(n, False, True)
149151

152+
void anal_with_stmt(self, WithStmt s):
153+
for n in s.name:
154+
if n:
155+
self.analyse_lvalue(n, False, True)
156+
150157
void anal_decorator(self, Decorator d):
151158
d.var._fullname = self.qualified_name(d.var.name())
152159
self.add_symbol(d.var.name(), SymbolTableNode(GDEF, d.var), d)
@@ -559,7 +566,7 @@ class SemanticAnalyzer(NodeVisitor):
559566
e.accept(self)
560567
for n in s.name:
561568
if n:
562-
self.add_var(n, s)
569+
self.analyse_lvalue(n)
563570
self.visit_block(s.body)
564571

565572
void visit_del_stmt(self, DelStmt s):

test/data/check-statements.test

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,3 +491,35 @@ class A:
491491
void __exit__(self, x, y, z): pass
492492
with A(): # E: "A" has no member "__enter__"
493493
pass
494+
495+
[case testWithStmtAndMultipleExprs]
496+
class A:
497+
void __enter__(self): pass
498+
void __exit__(self, x, y, z): pass
499+
class B:
500+
void __enter__(self): pass
501+
with A(), B(): # E: "B" has no member "__exit__"
502+
pass
503+
with B(), A(): # E: "B" has no member "__exit__"
504+
pass
505+
506+
[case testWithStmtAndResult]
507+
class B: pass
508+
class A:
509+
B __enter__(self): pass
510+
def __exit__(self, x, y, z): pass
511+
with A() as b:
512+
b = B()
513+
b = A() # E: Incompatible types in assignment
514+
515+
[case testWithStmtAndMultipleResults]
516+
class B: pass
517+
class C: pass
518+
class A<t>:
519+
t __enter__(self): pass
520+
def __exit__(self, x, y, z): pass
521+
with A<B>() as b, A<C>() as c:
522+
b = B()
523+
c = C()
524+
b = c # E: Incompatible types in assignment
525+
c = b # E: Incompatible types in assignment

test/data/semanal-statements.test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -575,12 +575,12 @@ MypyFile:1(
575575
Expr(
576576
NameExpr(object [builtins.object]))
577577
Name(
578-
Var(x))
578+
NameExpr(x* [__main__.x]))
579579
Block:1(
580580
ExpressionStmt:2(
581581
NameExpr(x [__main__.x])))))
582582

583-
[case testInFunction]
583+
[case testWithInFunction]
584584
def f():
585585
with f as x:
586586
x
@@ -593,7 +593,7 @@ MypyFile:1(
593593
Expr(
594594
NameExpr(f [__main__.f]))
595595
Name(
596-
Var(x))
596+
NameExpr(x* [l]))
597597
Block:2(
598598
ExpressionStmt:3(
599599
NameExpr(x [l])))))))
@@ -616,11 +616,11 @@ MypyFile:1(
616616
Expr(
617617
NameExpr(object [builtins.object]))
618618
Name(
619-
Var(a))
619+
NameExpr(a* [__main__.a]))
620620
Expr(
621621
NameExpr(object [builtins.object]))
622622
Name(
623-
Var(b))
623+
NameExpr(b* [__main__.b]))
624624
Block:3(
625625
PassStmt:4())))
626626

0 commit comments

Comments
 (0)