Skip to content

Commit 0451880

Browse files
authored
Allow redefinitions in except/else/finally (#18515)
Fixes #18514. Only `try` clause should be treated as fallible, this should not prevent `--allow-redefinition` from working in other try clauses (except, else, finally).
1 parent 1eb9d4c commit 0451880

File tree

3 files changed

+121
-3
lines changed

3 files changed

+121
-3
lines changed

mypy/renaming.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,21 @@ def visit_try_stmt(self, stmt: TryStmt) -> None:
152152
# type checker which allows them to be always redefined, so no need to
153153
# do renaming here.
154154
with self.enter_try():
155-
super().visit_try_stmt(stmt)
155+
stmt.body.accept(self)
156+
157+
for var, tp, handler in zip(stmt.vars, stmt.types, stmt.handlers):
158+
with self.enter_block():
159+
# Handle except variable together with its body
160+
if tp is not None:
161+
tp.accept(self)
162+
if var is not None:
163+
self.handle_def(var)
164+
for s in handler.body:
165+
s.accept(self)
166+
if stmt.else_body is not None:
167+
stmt.else_body.accept(self)
168+
if stmt.finally_body is not None:
169+
stmt.finally_body.accept(self)
156170

157171
def visit_with_stmt(self, stmt: WithStmt) -> None:
158172
for expr in stmt.expr:

test-data/unit/check-python311.test

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,47 @@ def foo():
259259
continue # E: "continue" not allowed in except* block
260260
return # E: "return" not allowed in except* block
261261
[builtins fixtures/exception.pyi]
262+
263+
[case testRedefineLocalWithinExceptStarTryClauses]
264+
# flags: --allow-redefinition
265+
def fn_str(_: str) -> int: ...
266+
def fn_int(_: int) -> None: ...
267+
def fn_exc(_: Exception) -> str: ...
268+
269+
def in_block() -> None:
270+
try:
271+
a = ""
272+
a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
273+
fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int"
274+
except* Exception:
275+
b = ""
276+
b = fn_str(b)
277+
fn_int(b)
278+
else:
279+
c = ""
280+
c = fn_str(c)
281+
fn_int(c)
282+
finally:
283+
d = ""
284+
d = fn_str(d)
285+
fn_int(d)
286+
reveal_type(a) # N: Revealed type is "builtins.str"
287+
reveal_type(b) # N: Revealed type is "builtins.int"
288+
reveal_type(c) # N: Revealed type is "builtins.int"
289+
reveal_type(d) # N: Revealed type is "builtins.int"
290+
291+
def across_blocks() -> None:
292+
try:
293+
a = ""
294+
except* Exception:
295+
a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
296+
else:
297+
a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
298+
reveal_type(a) # N: Revealed type is "builtins.str"
299+
300+
def exc_name() -> None:
301+
try:
302+
pass
303+
except* RuntimeError as e:
304+
e = fn_exc(e)
305+
[builtins fixtures/exception.pyi]

test-data/unit/check-redefine.test

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def h(a: Iterable[int]) -> None:
8888

8989
[case testCannotRedefineLocalWithinTry]
9090
# flags: --allow-redefinition
91+
def g(): pass
9192
def f() -> None:
9293
try:
9394
x = 0
@@ -102,7 +103,67 @@ def f() -> None:
102103
y
103104
y = ''
104105

105-
def g(): pass
106+
[case testRedefineLocalWithinTryClauses]
107+
# flags: --allow-redefinition
108+
def fn_str(_: str) -> int: ...
109+
def fn_int(_: int) -> None: ...
110+
111+
def in_block() -> None:
112+
try:
113+
a = ""
114+
a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
115+
fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int"
116+
except:
117+
b = ""
118+
b = fn_str(b)
119+
fn_int(b)
120+
else:
121+
c = ""
122+
c = fn_str(c)
123+
fn_int(c)
124+
finally:
125+
d = ""
126+
d = fn_str(d)
127+
fn_int(d)
128+
reveal_type(a) # N: Revealed type is "builtins.str"
129+
reveal_type(b) # N: Revealed type is "builtins.int"
130+
reveal_type(c) # N: Revealed type is "builtins.int"
131+
reveal_type(d) # N: Revealed type is "builtins.int"
132+
133+
def across_blocks() -> None:
134+
try:
135+
a = ""
136+
except:
137+
pass
138+
else:
139+
a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
140+
reveal_type(a) # N: Revealed type is "builtins.str"
141+
142+
[case testRedefineLocalExceptVar]
143+
# flags: --allow-redefinition
144+
def fn_exc(_: Exception) -> str: ...
145+
146+
def exc_name() -> None:
147+
try:
148+
pass
149+
except RuntimeError as e:
150+
e = fn_exc(e)
151+
[builtins fixtures/exception.pyi]
152+
153+
[case testRedefineNestedInTry]
154+
# flags: --allow-redefinition
155+
156+
def fn_int(_: int) -> None: ...
157+
158+
try:
159+
try:
160+
...
161+
finally:
162+
a = ""
163+
a = 5 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
164+
fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int"
165+
except:
166+
pass
106167

107168
[case testRedefineLocalWithinWith]
108169
# flags: --allow-redefinition
@@ -274,7 +335,6 @@ def f() -> None:
274335
# E: Incompatible types in assignment (expression has type "int", variable has type "TypeVar")
275336
reveal_type(x) # N: Revealed type is "typing.TypeVar"
276337
y = 1
277-
# NOTE: '"int" not callable' is due to test stubs
278338
y = TypeVar('y') # E: Cannot redefine "y" as a type variable \
279339
# E: Incompatible types in assignment (expression has type "TypeVar", variable has type "int")
280340
def h(a: y) -> y: return a # E: Variable "y" is not valid as a type \

0 commit comments

Comments
 (0)