Skip to content

Commit 0a5235a

Browse files
committed
Treat Literal["xyz"] as iterable
The extra condition that excluded LiteralType was introduced in #14827 I see no particular reason to have an instance check at all I was looking at this because of this comment from Emma #15511 (comment) Previously we errored with `"Literal['xy']" object is not iterable` which is of course totally false Now I issue the same error as in the str case, but restrict to cases where the unpack length does not match
1 parent c2a9642 commit 0a5235a

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

mypy/checker.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4117,7 +4117,18 @@ def check_multi_assignment(
41174117
)
41184118
else:
41194119
if isinstance(rvalue_type, Instance) and rvalue_type.type.fullname == "builtins.str":
4120+
if rvalue_type.last_known_value is None or (
4121+
isinstance(rvalue_type.last_known_value, str)
4122+
and len(rvalue_type.last_known_value) != len(lvalues)
4123+
):
4124+
self.msg.unpacking_strings_disallowed(context)
4125+
if (
4126+
isinstance(rvalue_type, LiteralType)
4127+
and isinstance(rvalue_type.value, str)
4128+
and len(lvalues) != len(rvalue_type.value)
4129+
):
41204130
self.msg.unpacking_strings_disallowed(context)
4131+
41214132
self.check_multi_assignment_from_iterable(
41224133
lvalues, rvalue_type, context, infer_lvalue_type
41234134
)
@@ -4363,9 +4374,7 @@ def check_multi_assignment_from_iterable(
43634374
infer_lvalue_type: bool = True,
43644375
) -> None:
43654376
rvalue_type = get_proper_type(rvalue_type)
4366-
if self.type_is_iterable(rvalue_type) and isinstance(
4367-
rvalue_type, (Instance, CallableType, TypeType, Overloaded)
4368-
):
4377+
if self.type_is_iterable(rvalue_type):
43694378
item_type = self.iterable_item_type(rvalue_type, context)
43704379
for lv in lvalues:
43714380
if isinstance(lv, StarExpr):
@@ -7803,9 +7812,7 @@ def note(
78037812
return
78047813
self.msg.note(msg, context, offset=offset, code=code)
78057814

7806-
def iterable_item_type(
7807-
self, it: Instance | CallableType | TypeType | Overloaded, context: Context
7808-
) -> Type:
7815+
def iterable_item_type(self, it: ProperType, context: Context) -> Type:
78097816
if isinstance(it, Instance):
78107817
iterable = map_instance_to_supertype(it, self.lookup_typeinfo("typing.Iterable"))
78117818
item_type = iterable.args[0]

test-data/unit/check-expressions.test

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,3 +2505,24 @@ a2, b2 = s # E: Unpacking a string is disallowed
25052505
reveal_type(a2) # N: Revealed type is "builtins.str"
25062506
reveal_type(b2) # N: Revealed type is "builtins.str"
25072507
[builtins fixtures/primitives.pyi]
2508+
2509+
[case testStringLiteralUnpacking]
2510+
from typing import Literal, Final
2511+
2512+
def takes_literal(xy: Literal["xy"]):
2513+
x, y = xy
2514+
reveal_type(x) # N: Revealed type is "builtins.str"
2515+
reveal_type(y) # N: Revealed type is "builtins.str"
2516+
2517+
x, y, z = xy # E: Unpacking a string is disallowed
2518+
reveal_type(z) # N: Revealed type is "builtins.str"
2519+
2520+
def last_known_value() -> None:
2521+
xy: Final = "xy"
2522+
x, y = xy
2523+
reveal_type(x) # N: Revealed type is "builtins.str"
2524+
reveal_type(y) # N: Revealed type is "builtins.str"
2525+
2526+
x, y, z = xy
2527+
reveal_type(z) # N: Revealed type is "builtins.str"
2528+
[builtins fixtures/primitives.pyi]

0 commit comments

Comments
 (0)