From 85310a4f69121b1965c536156207eabb678d566d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 24 Apr 2018 13:29:10 +0100 Subject: [PATCH 1/3] Fine-grained: Add tests for unpacking expressions (and fix crash) Also fix crash on incompatible dictionary unpack. Fixes #4959. Work towards #4951. --- mypy/messages.py | 2 +- test-data/unit/fine-grained.test | 84 ++++++++++++++++++++++++++++++++ test-data/unit/pythoneval.test | 11 +++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/mypy/messages.py b/mypy/messages.py index c58b2d84bdb2..df32a56a6b74 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -639,7 +639,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: # For function calls with keyword arguments, display the argument name rather than the # number. arg_label = str(n) - if isinstance(context, CallExpr): + if isinstance(context, CallExpr) and len(context.arg_names) >= n: arg_name = context.arg_names[n - 1] if arg_name is not None: arg_label = '"{}"'.format(arg_name) diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index f05ab4be56d2..e347793286d0 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -5800,3 +5800,87 @@ class M(type): [out] == a.py:2: error: Argument 1 to "f" of "M" has incompatible type "int"; expected "str" + +[case testExtendedUnpacking-skip-cache] +from typing import List +from a import g +def f() -> List[int]: + a, *b = g() + return b + +[file a.py] +from typing import Tuple +def g() -> Tuple[str, int, int]: pass + +[file a.py.2] +from typing import Tuple +def g() -> Tuple[str, str]: pass + +[builtins fixtures/tuple.pyi] +[out] +== +main:5: error: Incompatible return value type (got "List[str]", expected "List[int]") + +[case testUnpackInExpression1-skip-cache] +from typing import Tuple, List +from a import t + +def f() -> Tuple[int, int]: + return (1, *t()) + +def g() -> List[int]: + return [1, *t()] + +[file a.py] +from typing import Tuple +def t() -> Tuple[int]: ... + +[file a.py.2] +from typing import Tuple +def t() -> Tuple[str]: ... + +[builtins fixtures/list.pyi] +[out] +== +main:5: error: Incompatible return value type (got "Tuple[int, str]", expected "Tuple[int, int]") +main:8: error: List item 1 has incompatible type "Tuple[str]"; expected "int" + +[case testUnpackInExpression2-skip-cache] +from typing import Set +from a import t + +def f() -> Set[int]: + return {1, *t()} + +[file a.py] +from typing import Tuple +def t() -> Tuple[int]: pass + +[file a.py.2] +from typing import Tuple +def t() -> Tuple[str]: pass + +[builtins fixtures/set.pyi] +[out] +== +main:5: error: Argument 2 to has incompatible type "*Tuple[str]"; expected "int" + +[case testUnpackInExpression3-skip-cache] +from typing import Dict +from a import d + +def f() -> Dict[int, str]: + return {1: '', **d()} + +[file a.py] +from typing import Dict +def d() -> Dict[int, str]: pass + +[file a.py.2] +from typing import Dict +def d() -> Dict[int, int]: pass + +[builtins fixtures/dict.pyi] +[out] +== +main:5: error: Argument 1 to "update" of "dict" has incompatible type "Dict[int, int]"; expected "Mapping[int, str]" diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index e946f5e407ff..e193de93dffd 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1259,3 +1259,14 @@ class B: [out] _testInvalidSlots.py:2: error: Incompatible types in assignment (expression has type "int", base class "object" defined the type as "Union[str, Iterable[str], None]") _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]") + +[case testDictWithStarStarSpecialCase] +from typing import Dict + +def f() -> Dict[int, str]: + return {1: '', **d()} + +def d() -> Dict[int, int]: + return {} +[out] +_testDictWithStarStarSpecialCase.py:4: error: Argument 1 to "update" of "dict" has incompatible type "Dict[int, int]"; expected "Mapping[int, str]" From c4c4d015cb9755f24ceeaaa8d8a4b9fa63f732da Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 26 Apr 2018 17:13:16 +0100 Subject: [PATCH 2/3] Document fine-grained test variants and skipping --- test-data/unit/fine-grained.test | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 936bf0c69d5d..b128c238c670 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -34,6 +34,11 @@ -- -- Test runner can parse options from mypy.ini file. Updating this file in between -- incremental runs is not yet supported. +-- +-- Each test case run without caching and with caching (if the initial run passes), +-- unless it has one a --skip-cache or --skip-nocache suffix. We sometimes +-- skip caching test cases to speed up tests, if the caching variant is not useful. +-- The caching test case variants get an implicit _cached suffix. [case testReprocessFunction] import m From e9ff8aef1761f71fce910d7e070d8c3637715ade Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 26 Apr 2018 17:48:10 +0100 Subject: [PATCH 3/3] Support *x as lvalue --- mypy/server/deps.py | 3 ++- test-data/unit/deps.test | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mypy/server/deps.py b/mypy/server/deps.py index 9725a52929e5..b2b388e75d43 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -376,7 +376,8 @@ def process_lvalue(self, lvalue: Expression) -> None: elif isinstance(lvalue, TupleExpr): for item in lvalue.items: self.process_lvalue(item) - # TODO: star lvalue + elif isinstance(lvalue, StarExpr): + self.process_lvalue(lvalue.expr) def is_self_member_ref(self, memberexpr: MemberExpr) -> bool: """Does memberexpr to refer to an attribute of self?""" diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index a831cc4b1b56..db1655cac1f4 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1109,3 +1109,20 @@ class A(B): -> -> -> m, m.D + +[case testIndexedStarLvalue] +from typing import List, Tuple + +class B: + def __setitem__(self, i: int, v: List[str]) -> None: pass + +def g() -> Tuple[int, str, str]: pass + +def f(b: B) -> None: + a, *b[0] = g() +[builtins fixtures/list.pyi] +[out] + -> m.f + -> m.f + -> , m.B, m.f + -> m.f