Skip to content

Commit

Permalink
Make str unpacking check opt-in
Browse files Browse the repository at this point in the history
Fixes #13823. See also #6406
  • Loading branch information
hauntsaninja committed Jun 24, 2023
1 parent ef87305 commit 12cc931
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 18 deletions.
21 changes: 21 additions & 0 deletions docs/source/error_code_list2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -416,3 +416,24 @@ Example:
# The following will not generate an error on either
# Python 3.8, or Python 3.9
42 + "testing..." # type: ignore
.. _code-str-unpacking:

Check that ``str`` is explicitly unpacked [str-unpacking]
---------------------------------------------------------

It can sometimes be surprising that ``str`` is iterable, especially when unpacking.

Example:

.. code-block:: python
# Use "mypy --enable-error-code str-unpacking ..."
def print_dict(d: dict[str, str]) -> int:
# We meant to do d.items(), but instead we're unpacking the str keys of d
# Error: Unpacking a string is disallowed
for k, v in d:
print(k, v)
6 changes: 5 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3491,7 +3491,11 @@ def check_multi_assignment(
self.check_multi_assignment_from_union(
lvalues, rvalue, rvalue_type, context, infer_lvalue_type
)
elif isinstance(rvalue_type, Instance) and rvalue_type.type.fullname == "builtins.str":
elif (
self.msg.errors.is_error_code_enabled(codes.STR_UNPACKING)
and isinstance(rvalue_type, Instance)
and rvalue_type.type.fullname == "builtins.str"
):
self.msg.unpacking_strings_disallowed(context)
else:
self.check_multi_assignment_from_iterable(
Expand Down
3 changes: 3 additions & 0 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ def __hash__(self) -> int:
"General",
default_enabled=False,
)
STR_UNPACKING: Final[ErrorCode] = ErrorCode(
"str-unpacking", "Warn about expressions that unpack str", "General", default_enabled=False
)
NAME_MATCH: Final = ErrorCode(
"name-match", "Check that type definition has consistent naming", "General"
)
Expand Down
6 changes: 5 additions & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,11 @@ def wrong_number_values_to_unpack(
)

def unpacking_strings_disallowed(self, context: Context) -> None:
self.fail("Unpacking a string is disallowed", context)
self.fail(
"Unpacking a string is disallowed",
context,
code=codes.STR_UNPACKING,
)

def type_not_iterable(self, type: Type, context: Context) -> None:
self.fail(f"{format_type(type, self.options)} object is not iterable", context)
Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -2330,3 +2330,16 @@ T = TypeVar("T")
x: int
x + T # E: Unsupported operand types for + ("int" and "object")
T() # E: "object" not callable

[case testStringDisallowedUnpacking]
# flags: --enable-error-code str-unpacking
from typing import Dict

d: Dict[str, str]

for a, b in d: # E: Unpacking a string is disallowed
pass

s = "foo"
a, b = s # E: Unpacking a string is disallowed
[builtins fixtures/dict.pyi]
17 changes: 1 addition & 16 deletions test-data/unit/check-unions.test
Original file line number Diff line number Diff line change
Expand Up @@ -721,26 +721,11 @@ reveal_type(d) # N: Revealed type is "builtins.list[builtins.int]"
from typing import Union
bad: Union[int, str]

x, y = bad # E: "int" object is not iterable \
# E: Unpacking a string is disallowed
x, y = bad # E: "int" object is not iterable
reveal_type(x) # N: Revealed type is "Any"
reveal_type(y) # N: Revealed type is "Any"
[out]

[case testStringDisallowedUnpacking]
from typing import Dict

d: Dict[str, str]

for a, b in d: # E: Unpacking a string is disallowed
pass

s = "foo"
a, b = s # E: Unpacking a string is disallowed

[builtins fixtures/dict.pyi]
[out]

[case testUnionAlwaysTooMany]
from typing import Union, Tuple
bad: Union[Tuple[int, int, int], Tuple[str, str, str]]
Expand Down

0 comments on commit 12cc931

Please sign in to comment.