Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2b136f0

Browse files
authored
Narrow context fallback limitation to walrus only (#21294)
Fixes #21273 This fixes regression caused by too cautious check in assignment context fallback. Ternary expression can change binder version, but only assignment expression can have an _externally visible_ effect. So I add a dedicated counter to track this.
1 parent 4c68d1b commit 2b136f0

3 files changed

Lines changed: 23 additions & 2 deletions

File tree

mypy/checker.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,11 @@ def __init__(
547547
self._unique_id = 0
548548
self._variance_dummy_type = None
549549

550+
# Assignment expression is the only *expression* that can have externally
551+
# visible effects via binder. We bump this whenever this happens to handle
552+
# situations when some expressions are accepted multiple times.
553+
self.assignment_expression_effect = 0
554+
550555
@property
551556
def expr_checker(self) -> mypy.checkexpr.ExpressionChecker:
552557
return self._expr_checker
@@ -4772,7 +4777,7 @@ def infer_rvalue_with_fallback_context(
47724777
# context that is ultimately used. This is however tricky with redefinitions.
47734778
# For now we simply disable second accept in cases known to cause problems,
47744779
# see e.g. testAssignToOptionalTupleWalrus.
4775-
binder_version = self.binder.version
4780+
assignment_expression_effect = self.assignment_expression_effect
47764781

47774782
fallback_context_used = False
47784783
with (
@@ -4802,7 +4807,7 @@ def infer_rvalue_with_fallback_context(
48024807
union_fallback = (
48034808
preferred_context is not None
48044809
and isinstance(get_proper_type(lvalue_type), UnionType)
4805-
and binder_version == self.binder.version
4810+
and assignment_expression_effect == self.assignment_expression_effect
48064811
)
48074812

48084813
# Skip literal types, as they have special logic (for better errors).

mypy/checkexpr.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4461,7 +4461,10 @@ def check_list_multiply(self, e: OpExpr) -> Type:
44614461

44624462
def visit_assignment_expr(self, e: AssignmentExpr) -> Type:
44634463
value = self.accept(e.value)
4464+
binder_version = self.chk.binder.version
44644465
self.chk.check_assignment(e.target, e.value)
4466+
if self.chk.binder.version != binder_version:
4467+
self.chk.assignment_expression_effect += 1
44654468
self.chk.check_final(e)
44664469
if not has_uninhabited_component(value):
44674470
# TODO: can we get rid of this extra store_type()?

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,3 +1616,16 @@ async def outer(c: Cls[T]) -> Optional[T]:
16161616
return await inner(c)
16171617
[builtins fixtures/async_await.pyi]
16181618
[typing fixtures/typing-async.pyi]
1619+
1620+
[case testContextFallbackNarrowingTernaryRValue]
1621+
from typing import Iterable, Union
1622+
1623+
def foo(args: Union[Iterable[Union[str, int]], str, int]) -> Iterable[str]:
1624+
if isinstance(args, (str, int)):
1625+
args = (args,)
1626+
args = (
1627+
arg if isinstance(arg, str) else str()
1628+
for arg in args
1629+
)
1630+
return args
1631+
[builtins fixtures/isinstancelist.pyi]

0 commit comments

Comments
 (0)