From 4be232d3ccb52640b3117838c76eea941f2e7d86 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 9 Dec 2021 18:34:47 +0300 Subject: [PATCH 1/6] Fixes how `**` affects inference in `{}` expressions, refs #11691 --- mypy/checkexpr.py | 18 ++++++++++-------- mypy/join.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 728915622d71..00b44fa222ac 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3334,13 +3334,14 @@ def check_lst_expr(self, items: List[Expression], fullname: str, self.chk.named_generic_type(fullname, [tv]), self.named_type('builtins.function'), name=tag, - variables=[tv]) - out = self.check_call(constructor, - [(i.expr if isinstance(i, StarExpr) else i) - for i in items], - [(nodes.ARG_STAR if isinstance(i, StarExpr) else nodes.ARG_POS) - for i in items], - context)[0] + variables=[tv], + ) + out = self.check_call( + constructor, + [(i.expr if isinstance(i, StarExpr) else i) for i in items], + [(nodes.ARG_STAR if isinstance(i, StarExpr) else nodes.ARG_POS) for i in items], + context, + )[0] return remove_instance_last_known_values(out) def visit_tuple_expr(self, e: TupleExpr) -> Type: @@ -3518,7 +3519,8 @@ def visit_dict_expr(self, e: DictExpr) -> Type: variables=[kt, vt]) rv = self.check_call(constructor, [arg], [nodes.ARG_POS], arg)[0] else: - self.check_method_call_by_name('update', rv, [arg], [nodes.ARG_POS], arg) + arg_type = arg.accept(self) + rv = join.dict_unpack(rv, arg_type, self.named_type('typing.Mapping')) assert rv is not None return rv diff --git a/mypy/join.py b/mypy/join.py index e0d926f3fcf4..7056fb0babd5 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -597,3 +597,45 @@ def unpack_callback_protocol(t: Instance) -> Optional[Type]: if t.type.protocol_members == ['__call__']: return find_member('__call__', t, t, is_operator=True) return None + + +def dict_unpack(dict_obj: Type, other: Type, mapping_type: Instance) -> ProperType: + """Joins two dict-like objects. + + For example, `dict[str, int]` and `dict[int, int]` + will become `dict[object, int]`. + """ + dict_obj = get_proper_type(dict_obj) + if (not isinstance(dict_obj, Instance) + or dict_obj.type.fullname != 'builtins.dict' + or len(dict_obj.args) != 2): + # Since this function is only used when two dicts are joined like: + # `{**other, 'a': 1}`, we require `dict_obj` to be an `Instance` + # of `builtins.dict``. + return AnyType(TypeOfAny.from_error) + + key_type, value_type = dict_obj.args + new_key_type, new_value_type = extract_key_value_types(other, mapping_type) + return dict_obj.copy_modified(args=[ + join_types(key_type, new_key_type), + join_types(value_type, new_value_type), + ]) + + +def extract_key_value_types(typ: Type, mapping_type: Instance) -> Tuple[Type, Type]: + typ = get_proper_type(typ) + if isinstance(typ, Instance) and len(typ.args) >= 2: + mapping = map_instance_to_supertype(typ, mapping_type.type) + return mapping.args[0], mapping.args[1] + elif isinstance(typ, UnionType): + keys = [] + values = [] + for item in typ.relevant_items: + key, value = extract_key_value_types(item, mapping_type) + keys.append(key) + values.append(value) + return join_type_list(keys), join_type_list(values) + return ( + AnyType(TypeOfAny.implementation_artifact), + AnyType(TypeOfAny.implementation_artifact), + ) From 540f00b613c1a66a30f13dd7fc1406ec1d57a90f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 9 Dec 2021 22:52:06 +0300 Subject: [PATCH 2/6] Fixes typechecking --- mypy/join.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/join.py b/mypy/join.py index 7056fb0babd5..737adf82813a 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -630,7 +630,7 @@ def extract_key_value_types(typ: Type, mapping_type: Instance) -> Tuple[Type, Ty elif isinstance(typ, UnionType): keys = [] values = [] - for item in typ.relevant_items: + for item in typ.relevant_items(): key, value = extract_key_value_types(item, mapping_type) keys.append(key) values.append(value) From edf20f55a186e61f7530e8eded78146db4126e73 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 10 Dec 2021 14:00:36 +0300 Subject: [PATCH 3/6] Adds tests --- mypy/checkexpr.py | 62 ++++++++++- mypy/join.py | 42 ------- test-data/unit/check-expressions.test | 155 ++++++++++++++++++++++++++ 3 files changed, 216 insertions(+), 43 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 00b44fa222ac..fb5357868940 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3520,10 +3520,70 @@ def visit_dict_expr(self, e: DictExpr) -> Type: rv = self.check_call(constructor, [arg], [nodes.ARG_POS], arg)[0] else: arg_type = arg.accept(self) - rv = join.dict_unpack(rv, arg_type, self.named_type('typing.Mapping')) + rv = self.dict_unpack(rv, arg_type, arg) assert rv is not None return rv + def dict_unpack(self, dict_obj: Type, other: Type, context: Context) -> ProperType: + """Joins two dict-like objects. + + For example, `dict[str, int]` and `dict[int, int]` + will become `dict[object, int]`. + """ + dict_obj = get_proper_type(dict_obj) + if (not isinstance(dict_obj, Instance) + or dict_obj.type.fullname != 'builtins.dict' + or len(dict_obj.args) != 2): + # Since this function is only used when two dicts are joined like: + # `{**other, 'a': 1}`, we require `dict_obj` to be an `Instance` + # of `builtins.dict``. This should not happen, unless someone else + # will call this function from other places. + return AnyType(TypeOfAny.from_error) + + key_type, value_type = dict_obj.args + new_key_type, new_value_type = self.extract_key_value_types(other, context) + return dict_obj.copy_modified(args=[ + join.join_types(key_type, new_key_type), + join.join_types(value_type, new_value_type), + ]) + + def extract_key_value_types(self, typ: Type, context: Context) -> Tuple[Type, Type]: + mapping_type = self.named_type('typing.Mapping') + + typ = get_proper_type(typ) + print(typ, type(typ)) + if (isinstance(typ, Instance) + and is_subtype(typ, mapping_type, ignore_type_params=True)): + mapping = map_instance_to_supertype(typ, mapping_type.type) + if len(mapping.args) >= 2: + return mapping.args[0], mapping.args[1] + elif isinstance(typ, TypedDictType): + print(list(typ.items.values())) + return ( + self.named_type('builtins.str'), + join.join_type_list(list(typ.items.values())), + ) + elif isinstance(typ, UnionType): + keys = [] + values = [] + for item in typ.relevant_items(): + key, value = self.extract_key_value_types(item, context) + keys.append(key) + values.append(value) + return join.join_type_list(keys), join.join_type_list(values) + elif isinstance(typ, AnyType): + return ( + AnyType(TypeOfAny.implementation_artifact), + AnyType(TypeOfAny.implementation_artifact), + ) + + # All other types are not allowed to be unpacked. + self.chk.fail('Cannot unpack "{}" type with "**"'.format(typ), context) + return ( + AnyType(TypeOfAny.from_error), + AnyType(TypeOfAny.from_error), + ) + def find_typeddict_context(self, context: Optional[Type], dict_expr: DictExpr) -> Optional[TypedDictType]: context = get_proper_type(context) diff --git a/mypy/join.py b/mypy/join.py index 737adf82813a..e0d926f3fcf4 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -597,45 +597,3 @@ def unpack_callback_protocol(t: Instance) -> Optional[Type]: if t.type.protocol_members == ['__call__']: return find_member('__call__', t, t, is_operator=True) return None - - -def dict_unpack(dict_obj: Type, other: Type, mapping_type: Instance) -> ProperType: - """Joins two dict-like objects. - - For example, `dict[str, int]` and `dict[int, int]` - will become `dict[object, int]`. - """ - dict_obj = get_proper_type(dict_obj) - if (not isinstance(dict_obj, Instance) - or dict_obj.type.fullname != 'builtins.dict' - or len(dict_obj.args) != 2): - # Since this function is only used when two dicts are joined like: - # `{**other, 'a': 1}`, we require `dict_obj` to be an `Instance` - # of `builtins.dict``. - return AnyType(TypeOfAny.from_error) - - key_type, value_type = dict_obj.args - new_key_type, new_value_type = extract_key_value_types(other, mapping_type) - return dict_obj.copy_modified(args=[ - join_types(key_type, new_key_type), - join_types(value_type, new_value_type), - ]) - - -def extract_key_value_types(typ: Type, mapping_type: Instance) -> Tuple[Type, Type]: - typ = get_proper_type(typ) - if isinstance(typ, Instance) and len(typ.args) >= 2: - mapping = map_instance_to_supertype(typ, mapping_type.type) - return mapping.args[0], mapping.args[1] - elif isinstance(typ, UnionType): - keys = [] - values = [] - for item in typ.relevant_items(): - key, value = extract_key_value_types(item, mapping_type) - keys.append(key) - values.append(value) - return join_type_list(keys), join_type_list(values) - return ( - AnyType(TypeOfAny.implementation_artifact), - AnyType(TypeOfAny.implementation_artifact), - ) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 70fda50d617e..067b234c04d7 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1786,6 +1786,161 @@ f = {**b} # type: Dict[int, int] # E: List item 0 has incompatible type "Dict[ [builtins fixtures/dict.pyi] [typing fixtures/typing-medium.pyi] +[case testDictExprWithStarAdvanced] +# Same type: +foo = {1: "a"} + +reveal_type({**foo}) # N: Revealed type is "builtins.dict[builtins.int*, builtins.str*]" +reveal_type({**foo, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" +reveal_type({**foo, 2: 'b', 3: 'c'}) # N: Revealed type is "builtins.dict[builtins.int*, builtins.str*]" +reveal_type({2: 'b', **foo}) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" +reveal_type({2: 'b', 3: 'c', **foo}) # N: Revealed type is "builtins.dict[builtins.int*, builtins.str*]" +reveal_type({2: 'b', **foo, 3: 'c'}) # N: Revealed type is "builtins.dict[builtins.int*, builtins.str*]" +reveal_type({2: 'b', **foo, 3: 'c', **foo}) # N: Revealed type is "builtins.dict[builtins.int*, builtins.str*]" + + +# Changing type: +bar = {'a': 1} + +reveal_type({**bar, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" +reveal_type({**bar, 2: 'b', 3: 'c'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" +reveal_type({2: 'b', **bar}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" +reveal_type({2: 'b', 3: 'c', **bar}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" +reveal_type({2: 'b', **bar, 3: 'c'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" +reveal_type({2: 'b', **bar, 3: 'c', **bar}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" + + +# With subtypes: +class A: pass +class B(A): pass + +parent_dict = {A(): A()} +child_dict = {B(): B()} +mixed_dict = {A(): B()} + +reveal_type({**parent_dict, A(): A()}) # N: Revealed type is "builtins.dict[__main__.A*, __main__.A*]" +reveal_type({**parent_dict, B(): B()}) # N: Revealed type is "builtins.dict[__main__.A, __main__.A]" +reveal_type({A(): B(), **parent_dict}) # N: Revealed type is "builtins.dict[__main__.A, __main__.A]" + +reveal_type({**child_dict, A(): A()}) # N: Revealed type is "builtins.dict[__main__.A, __main__.A]" +reveal_type({**child_dict, B(): B()}) # N: Revealed type is "builtins.dict[__main__.B*, __main__.B*]" +reveal_type({**child_dict, A(): B()}) # N: Revealed type is "builtins.dict[__main__.A, __main__.B]" + +reveal_type({**mixed_dict, A(): A()}) # N: Revealed type is "builtins.dict[__main__.A, __main__.A]" +reveal_type({**mixed_dict, B(): B()}) # N: Revealed type is "builtins.dict[__main__.A, __main__.B]" +reveal_type({**mixed_dict, A(): B()}) # N: Revealed type is "builtins.dict[__main__.A*, __main__.B*]" +reveal_type({**mixed_dict, B(): A()}) # N: Revealed type is "builtins.dict[__main__.A, __main__.A]" +[builtins fixtures/dict.pyi] + + +[case testDictUnpackDifferentTypes] +from typing import Any, Union, Dict, Mapping, TypeVar, Generic, TypedDict, NoReturn + +K = TypeVar('K') +V = TypeVar('V') + +class MyClass: pass +class MyGeneric1(Generic[K]): pass +class MyGeneric2(Generic[K, V]): pass +class MyDict(Dict[K, V]): pass +class MyMapping(Mapping[K, V]): pass +class MyMapping2(Generic[K, V], Mapping[V, K]): pass +class MyMapping3(Mapping[str, int]): pass + +class MySimpleTypedDict(TypedDict): + arg: int + +class MyComplexTypedDict(TypedDict): + arg: int + t: type + + +# Correct types: + +d = {'a': 1} + +md: MyDict[str, int] +mm: MyMapping[str, int] +mm2: MyMapping2[int, str] # reversed +mm3: MyMapping3 +mstd: MySimpleTypedDict +mctd: MyComplexTypedDict + +reveal_type({**md}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.int*]" +reveal_type({**md, 'b': 2}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type({**md, 2: 2}) # N: Revealed type is "builtins.dict[builtins.object, builtins.int]" +reveal_type({**md, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" +reveal_type({**md, **d}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type({**d, **md}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + +reveal_type({**mm}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.int*]" +reveal_type({**mm, 'b': 2}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type({**mm, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" + +reveal_type({**mm2}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.int*]" +reveal_type({**mm2, 'b': 2}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type({**mm2, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" + +reveal_type({**mm3}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.int*]" +reveal_type({**mm3, 'b': 2}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type({**mm3, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" + +# TODO: this type for `**mstd` does not seem correct, but this issue is not related to +# https://github.com/python/mypy/pull/11693 +reveal_type({**mstd}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.object*]" +reveal_type({**mstd, 'b': 2}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type({**mstd, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" + +reveal_type({**mctd}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.object*]" +reveal_type({**mctd, 'b': 2}) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +reveal_type({**mctd, 2: 'b'}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" + +a: Any +reveal_type({**a}) # N: Revealed type is "builtins.dict[Any, Any]" +reveal_type({**a, **d}) # N: Revealed type is "builtins.dict[Any, Any]" +reveal_type({'a': 1, **a}) # N: Revealed type is "builtins.dict[Any, Any]" + +u1: Union[MyDict[str, int], MyMapping[str, int], Dict[str, int]] +reveal_type({**u1}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.int*]" +reveal_type({**u1, **d}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type({'a': 1, **d}) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + +u2: Union[MyDict[str, int], MyDict[int, str]] +reveal_type({'a': 1, **u2}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" + + +# Wrong types: + +mc: MyClass +mg1: MyGeneric1[str] +mg2: MyGeneric2[str, int] +never: NoReturn +un1: Union[MyClass, Dict[str, int]] +un2: Union[MyClass, MyGeneric1[str]] + + +reveal_type({1: 'a', **mc}) # N: Revealed type is "builtins.dict[Any, Any]" \ + # E: Cannot unpack "__main__.MyClass" type with "**" + +reveal_type({1: 'a', **mg1}) # N: Revealed type is "builtins.dict[Any, Any]" \ + # E: Cannot unpack "__main__.MyGeneric1[builtins.str]" type with "**" + +reveal_type({1: 'a', **mg2}) # N: Revealed type is "builtins.dict[Any, Any]" \ + # E: Cannot unpack "__main__.MyGeneric2[builtins.str, builtins.int]" type with "**" + +reveal_type({1: 'a', **never}) # N: Revealed type is "builtins.dict[Any, Any]" \ + # E: Cannot unpack "" type with "**" + +reveal_type({1: 'a', **un1}) # N: Revealed type is "builtins.dict[Any, Any]" \ + # E: Cannot unpack "__main__.MyClass" type with "**" + +reveal_type({1: 'a', **un2}) # N: Revealed type is "builtins.dict[Any, Any]" \ + # E: Cannot unpack "__main__.MyClass" type with "**" \ + # E: Cannot unpack "__main__.MyGeneric1[builtins.str]" type with "**" + +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [case testDictIncompatibleTypeErrorMessage] from typing import Dict, Callable From a233afcd5d1a8de072bd67ca56d40e14c77bf4e1 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 10 Dec 2021 20:31:27 +0300 Subject: [PATCH 4/6] Removes prints --- mypy/checkexpr.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index fb5357868940..eec4242a2aea 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3551,14 +3551,12 @@ def extract_key_value_types(self, typ: Type, context: Context) -> Tuple[Type, Ty mapping_type = self.named_type('typing.Mapping') typ = get_proper_type(typ) - print(typ, type(typ)) if (isinstance(typ, Instance) and is_subtype(typ, mapping_type, ignore_type_params=True)): mapping = map_instance_to_supertype(typ, mapping_type.type) if len(mapping.args) >= 2: return mapping.args[0], mapping.args[1] elif isinstance(typ, TypedDictType): - print(list(typ.items.values())) return ( self.named_type('builtins.str'), join.join_type_list(list(typ.items.values())), From 164a28c467e6232e45f3a035c8aaa6dc22fb2c46 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 10 Dec 2021 22:57:27 +0300 Subject: [PATCH 5/6] Fixes tests --- mypy/checkexpr.py | 16 ++++++++-------- test-data/unit/check-expressions.test | 6 +++++- test-data/unit/fine-grained.test | 2 +- test-data/unit/pythoneval.test | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index eec4242a2aea..75e2e068716c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3531,14 +3531,14 @@ def dict_unpack(self, dict_obj: Type, other: Type, context: Context) -> ProperTy will become `dict[object, int]`. """ dict_obj = get_proper_type(dict_obj) - if (not isinstance(dict_obj, Instance) - or dict_obj.type.fullname != 'builtins.dict' - or len(dict_obj.args) != 2): - # Since this function is only used when two dicts are joined like: - # `{**other, 'a': 1}`, we require `dict_obj` to be an `Instance` - # of `builtins.dict``. This should not happen, unless someone else - # will call this function from other places. - return AnyType(TypeOfAny.from_error) + + # Since this function is only used when two dicts are joined like: + # `{**other, 'a': 1}`, we require `dict_obj` to be an `Instance` + # of `builtins.dict``. This should not happen, unless someone else + # will call this function from other places. + assert isinstance(dict_obj, Instance) + assert dict_obj.type.fullname == 'builtins.dict' + assert len(dict_obj.args) == 2 key_type, value_type = dict_obj.args new_key_type, new_value_type = self.extract_key_value_types(other, context) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 067b234c04d7..9aca7072de69 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1781,7 +1781,7 @@ a = {'a': 1} b = {'z': 26, **a} c = {**b} d = {**a, **b, 'c': 3} -e = {1: 'a', **a} # E: Argument 1 to "update" of "dict" has incompatible type "Dict[str, int]"; expected "Mapping[int, str]" +e = {1: 'a', **a} f = {**b} # type: Dict[int, int] # E: List item 0 has incompatible type "Dict[str, int]"; expected "Mapping[int, int]" [builtins fixtures/dict.pyi] [typing fixtures/typing-medium.pyi] @@ -1908,6 +1908,10 @@ reveal_type({'a': 1, **d}) # N: Revealed type is "builtins.dict[builtins.str, b u2: Union[MyDict[str, int], MyDict[int, str]] reveal_type({'a': 1, **u2}) # N: Revealed type is "builtins.dict[builtins.object, builtins.object]" +u3: Union[MyComplexTypedDict, MySimpleTypedDict] +reveal_type({**u3}) # N: Revealed type is "builtins.dict[builtins.str*, builtins.object*]" +reveal_type({'a': 1, **u3}) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" + # Wrong types: diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 18310e172a9c..0fa40e495476 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -7395,7 +7395,7 @@ 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]" +main:5: error: Incompatible return value type (got "Dict[int, object]", expected "Dict[int, str]") [case testAwaitAndAsyncDef-only_when_nocache] from a import g diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 90588a86d8bf..a3ea8f6c678e 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1328,7 +1328,7 @@ def f() -> Dict[int, str]: 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]" +_testDictWithStarStarSpecialCase.py:4: error: Incompatible return value type (got "Dict[int, object]", expected "Dict[int, str]") [case testLoadsOfOverloads] from typing import overload, Any, TypeVar, Iterable, List, Dict, Callable, Union From 167df5fde3eaaa35d150a16695fa630e85080ba7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 19 Dec 2021 17:21:01 +0300 Subject: [PATCH 6/6] Update checkexpr.py --- mypy/checkexpr.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 75e2e068716c..cec2ef86b94b 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3504,7 +3504,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: else: # dict(...) will be called below. pass - # Call rv.update(arg) for each arg in **stargs, + # Re-infer types for each `**` unpack in dict args, # except if rv isn't set yet, then set rv = dict(arg). if stargs: for arg in stargs: @@ -3519,8 +3519,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: variables=[kt, vt]) rv = self.check_call(constructor, [arg], [nodes.ARG_POS], arg)[0] else: - arg_type = arg.accept(self) - rv = self.dict_unpack(rv, arg_type, arg) + rv = self.dict_unpack(rv, arg.accept(self), arg) assert rv is not None return rv