Skip to content

Commit 7b257c8

Browse files
authored
Fine-grained: Support Python 3 unpacking expressions (and fix crash) (#4966)
Also fix crash on incompatible dictionary unpack. Fixes #4959. Work towards #4951.
1 parent d6a22cf commit 7b257c8

File tree

5 files changed

+120
-2
lines changed

5 files changed

+120
-2
lines changed

mypy/messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type:
645645
# For function calls with keyword arguments, display the argument name rather than the
646646
# number.
647647
arg_label = str(n)
648-
if isinstance(context, CallExpr):
648+
if isinstance(context, CallExpr) and len(context.arg_names) >= n:
649649
arg_name = context.arg_names[n - 1]
650650
if arg_name is not None:
651651
arg_label = '"{}"'.format(arg_name)

mypy/server/deps.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,8 @@ def process_lvalue(self, lvalue: Expression) -> None:
376376
elif isinstance(lvalue, TupleExpr):
377377
for item in lvalue.items:
378378
self.process_lvalue(item)
379-
# TODO: star lvalue
379+
elif isinstance(lvalue, StarExpr):
380+
self.process_lvalue(lvalue.expr)
380381

381382
def is_self_member_ref(self, memberexpr: MemberExpr) -> bool:
382383
"""Does memberexpr to refer to an attribute of self?"""

test-data/unit/deps.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,3 +1109,20 @@ class A(B):
11091109
<mod.C.__new__> -> <m.D.__new__>
11101110
<mod.C.x> -> <m.D.x>
11111111
<mod.C> -> m, m.D
1112+
1113+
[case testIndexedStarLvalue]
1114+
from typing import List, Tuple
1115+
1116+
class B:
1117+
def __setitem__(self, i: int, v: List[str]) -> None: pass
1118+
1119+
def g() -> Tuple[int, str, str]: pass
1120+
1121+
def f(b: B) -> None:
1122+
a, *b[0] = g()
1123+
[builtins fixtures/list.pyi]
1124+
[out]
1125+
<m.B.__getitem__> -> m.f
1126+
<m.B.__setitem__> -> m.f
1127+
<m.B> -> <m.f>, m.B, m.f
1128+
<m.g> -> m.f

test-data/unit/fine-grained.test

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
--
3535
-- Test runner can parse options from mypy.ini file. Updating this file in between
3636
-- incremental runs is not yet supported.
37+
--
38+
-- Each test case run without caching and with caching (if the initial run passes),
39+
-- unless it has one a --skip-cache or --skip-nocache suffix. We sometimes
40+
-- skip caching test cases to speed up tests, if the caching variant is not useful.
41+
-- The caching test case variants get an implicit _cached suffix.
3742

3843
[case testReprocessFunction]
3944
import m
@@ -5801,6 +5806,90 @@ class M(type):
58015806
==
58025807
a.py:2: error: Argument 1 to "f" of "M" has incompatible type "int"; expected "str"
58035808

5809+
[case testExtendedUnpacking-skip-cache]
5810+
from typing import List
5811+
from a import g
5812+
def f() -> List[int]:
5813+
a, *b = g()
5814+
return b
5815+
5816+
[file a.py]
5817+
from typing import Tuple
5818+
def g() -> Tuple[str, int, int]: pass
5819+
5820+
[file a.py.2]
5821+
from typing import Tuple
5822+
def g() -> Tuple[str, str]: pass
5823+
5824+
[builtins fixtures/tuple.pyi]
5825+
[out]
5826+
==
5827+
main:5: error: Incompatible return value type (got "List[str]", expected "List[int]")
5828+
5829+
[case testUnpackInExpression1-skip-cache]
5830+
from typing import Tuple, List
5831+
from a import t
5832+
5833+
def f() -> Tuple[int, int]:
5834+
return (1, *t())
5835+
5836+
def g() -> List[int]:
5837+
return [1, *t()]
5838+
5839+
[file a.py]
5840+
from typing import Tuple
5841+
def t() -> Tuple[int]: ...
5842+
5843+
[file a.py.2]
5844+
from typing import Tuple
5845+
def t() -> Tuple[str]: ...
5846+
5847+
[builtins fixtures/list.pyi]
5848+
[out]
5849+
==
5850+
main:5: error: Incompatible return value type (got "Tuple[int, str]", expected "Tuple[int, int]")
5851+
main:8: error: List item 1 has incompatible type "Tuple[str]"; expected "int"
5852+
5853+
[case testUnpackInExpression2-skip-cache]
5854+
from typing import Set
5855+
from a import t
5856+
5857+
def f() -> Set[int]:
5858+
return {1, *t()}
5859+
5860+
[file a.py]
5861+
from typing import Tuple
5862+
def t() -> Tuple[int]: pass
5863+
5864+
[file a.py.2]
5865+
from typing import Tuple
5866+
def t() -> Tuple[str]: pass
5867+
5868+
[builtins fixtures/set.pyi]
5869+
[out]
5870+
==
5871+
main:5: error: Argument 2 to <set> has incompatible type "*Tuple[str]"; expected "int"
5872+
5873+
[case testUnpackInExpression3-skip-cache]
5874+
from typing import Dict
5875+
from a import d
5876+
5877+
def f() -> Dict[int, str]:
5878+
return {1: '', **d()}
5879+
5880+
[file a.py]
5881+
from typing import Dict
5882+
def d() -> Dict[int, str]: pass
5883+
5884+
[file a.py.2]
5885+
from typing import Dict
5886+
def d() -> Dict[int, int]: pass
5887+
5888+
[builtins fixtures/dict.pyi]
5889+
[out]
5890+
==
5891+
main:5: error: Argument 1 to "update" of "dict" has incompatible type "Dict[int, int]"; expected "Mapping[int, str]"
5892+
58045893
[case testAwaitAndAsyncDef-skip-cache]
58055894
from a import g
58065895

test-data/unit/pythoneval.test

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,3 +1259,14 @@ class B:
12591259
[out]
12601260
_testInvalidSlots.py:2: error: Incompatible types in assignment (expression has type "int", base class "object" defined the type as "Union[str, Iterable[str], None]")
12611261
_testInvalidSlots.py:4: error: Incompatible types in assignment (expression has type "Tuple[int, int]", base class "object" defined the type as "Union[str, Iterable[str], None]")
1262+
1263+
[case testDictWithStarStarSpecialCase]
1264+
from typing import Dict
1265+
1266+
def f() -> Dict[int, str]:
1267+
return {1: '', **d()}
1268+
1269+
def d() -> Dict[int, int]:
1270+
return {}
1271+
[out]
1272+
_testDictWithStarStarSpecialCase.py:4: error: Argument 1 to "update" of "dict" has incompatible type "Dict[int, int]"; expected "Mapping[int, str]"

0 commit comments

Comments
 (0)