Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
ListComprehension, GeneratorExpr, SetExpr, MypyFile, Decorator,
ConditionalExpr, ComparisonExpr, TempNode, SetComprehension,
DictionaryComprehension, ComplexExpr, EllipsisExpr,
TypeAliasExpr, BackquoteExpr, ARG_POS
TypeAliasExpr, BackquoteExpr, ARG_POS, ARG_NAMED, ARG_STAR2
)
from mypy.nodes import function_type
from mypy import nodes
Expand Down Expand Up @@ -422,6 +422,20 @@ def infer_function_type_arguments(self, callee_type: CallableType,
inferred_args) = self.infer_function_type_arguments_pass2(
callee_type, args, arg_kinds, formal_to_actual,
inferred_args, context)

if callee_type.special_sig == 'dict' and len(inferred_args) == 2 and (
ARG_NAMED in arg_kinds or ARG_STAR2 in arg_kinds):
# HACK: Infer str key type for dict(...) with keyword args. The type system
# can't represent this so we special case it, as this is a pretty common
# thing. This doesn't quite work with all possible subclasses of dict
# if they shuffle type variables around, as we assume that there is a 1-1
# correspondence with dict type variables. This is a marginal issue and
# a little tricky to fix so it's left unfixed for now.
if isinstance(inferred_args[0], NoneTyp):
inferred_args[0] = self.named_type('builtins.str')
elif not is_subtype(self.named_type('builtins.str'), inferred_args[0]):
self.msg.fail(messages.KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE,
context)
else:
# In dynamically typed functions use implicit 'Any' types for
# type variables.
Expand Down
20 changes: 14 additions & 6 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Type checking of attribute access"""

from typing import cast, Callable, List
from typing import cast, Callable, List, Optional

from mypy.types import (
Type, Instance, AnyType, TupleType, CallableType, FunctionLike, TypeVarDef,
Expand Down Expand Up @@ -350,7 +350,7 @@ def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) ->
arg_names=["_args", "_kwds"],
ret_type=AnyType(),
fallback=builtin_type('builtins.function'))
return class_callable(sig, info, fallback)
return class_callable(sig, info, fallback, None)
# Construct callable type based on signature of __init__. Adjust
# return type and insert type arguments.
return type_object_type_from_function(init_method, info, fallback)
Expand All @@ -372,17 +372,24 @@ def type_object_type_from_function(init_or_new: FuncBase, info: TypeInfo,
signature = cast(FunctionLike,
map_type_from_supertype(signature, info, init_or_new.info))

if init_or_new.info.fullname() == 'builtins.dict':
# Special signature!
special_sig = 'dict'
else:
special_sig = None

if isinstance(signature, CallableType):
return class_callable(signature, info, fallback)
return class_callable(signature, info, fallback, special_sig)
else:
# Overloaded __init__/__new__.
items = [] # type: List[CallableType]
for item in cast(Overloaded, signature).items():
items.append(class_callable(item, info, fallback))
items.append(class_callable(item, info, fallback, special_sig))
return Overloaded(items)


def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance) -> CallableType:
def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance,
special_sig: Optional[str]) -> CallableType:
"""Create a type object type based on the signature of __init__."""
variables = [] # type: List[TypeVarDef]
for i, tvar in enumerate(info.defn.type_vars):
Expand All @@ -393,7 +400,8 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance)
variables.extend(initvars)

callable_type = init_type.copy_modified(
ret_type=self_type(info), fallback=type_type, name=None, variables=variables)
ret_type=self_type(info), fallback=type_type, name=None, variables=variables,
special_sig=special_sig)
c = callable_type.with_name('"{}"'.format(info.name()))
cc = convert_class_tvars_to_func_tvars(c, len(initvars))
cc.is_classmethod_class = True
Expand Down
1 change: 0 additions & 1 deletion mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ def infer_constraints_for_callable(

Return a list of constraints.
"""

constraints = [] # type: List[Constraint]
tuple_counter = [0]

Expand Down
2 changes: 2 additions & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@
FUNCTION_TYPE_EXPECTED = "Function is missing a type annotation"
RETURN_TYPE_EXPECTED = "Function is missing a return type annotation"
ARGUMENT_TYPE_EXPECTED = "Function is missing a type annotation for one or more arguments"
KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE = \
'Keyword argument only valid with "str" key type in call to "dict"'


class MessageBuilder:
Expand Down
108 changes: 101 additions & 7 deletions mypy/test/data/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -1340,7 +1340,6 @@ def f(x: int) -> Iterator[None]:
-- ----------------



[case testYieldFromIteratorHasNoValue]
from typing import Iterator
def f() -> Iterator[int]:
Expand All @@ -1362,12 +1361,14 @@ def g() -> Iterator[int]:
[out]


-- dict(...)
-- ---------

-- dict(x=y, ...)
-- --------------

-- Note that the stub used in unit tests does not have all overload
-- variants, but it should not matter.

[case testDictWithKeywordArgs]
[case testDictWithKeywordArgsOnly]
from typing import Dict, Any
d1 = dict(a=1, b=2) # type: Dict[str, int]
d2 = dict(a=1, b='') # type: Dict[str, int] # E: List item 1 has incompatible type "Tuple[str, str]"
Expand All @@ -1378,7 +1379,100 @@ d5 = dict(a=1, b='') # type: Dict[str, Any]
[builtins fixtures/dict.py]

[case testDictWithoutKeywordArgs]
dict(undefined)
from typing import Dict
d = dict() # E: Need type annotation for variable
d2 = dict() # type: Dict[int, str]
dict(undefined) # E: Name 'undefined' is not defined
[builtins fixtures/dict.py]

[case testDictFromList]
from typing import Dict
d = dict([(1, 'x'), (2, 'y')])
d() # E: Dict[int, str] not callable
d2 = dict([(1, 'x')]) # type: Dict[str, str] # E: List item 0 has incompatible type "Tuple[int, str]"
[builtins fixtures/dict.py]

[case testDictFromIterableAndKeywordArg]
from typing import Dict
it = [('x', 1)]

d = dict(it, x=1)
d() # E: Dict[str, int] not callable

d2 = dict(it, x='') # E: Cannot infer type argument 2 of "dict"
d2() # E: Dict[Any, Any] not callable

d3 = dict(it, x='') # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type "str"; expected "int"
[builtins fixtures/dict.py]

[case testDictFromIterableAndKeywordArg2]
it = [(1, 'x')]
dict(it, x='y') # E: Keyword argument only valid with "str" key type in call to "dict"
[builtins fixtures/dict.py]

[case testDictFromIterableAndKeywordArg3]
d = dict([], x=1)
d() # E: Dict[str, int] not callable
[builtins fixtures/dict.py]

[case testDictFromIterableAndStarStarArgs]
from typing import Dict
it = [('x', 1)]

kw = {'x': 1}
d = dict(it, **kw)
d() # E: Dict[str, int] not callable

kw2 = {'x': ''}
d2 = dict(it, **kw2) # E: Cannot infer type argument 2 of "dict"
d2() # E: Dict[Any, Any] not callable

d3 = dict(it, **kw2) # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type **Dict[str, str]; expected "int"
[builtins fixtures/dict.py]

[case testDictFromIterableAndStarStarArgs2]
it = [(1, 'x')]
kw = {'x': 'y'}
d = dict(it, **kw) # E: Keyword argument only valid with "str" key type in call to "dict"
d() # E: Dict[int, str] not callable
[builtins fixtures/dict.py]

[case testUserDefinedClassNamedDict]
from typing import Generic, TypeVar
T = TypeVar('T')
S = TypeVar('S')
class dict(Generic[T, S]):
def __init__(self, x: T, **kwargs: T) -> None: pass
dict(1, y=1)
[builtins fixtures/dict.py]

[case testSpecialSignatureForSubclassOfDict]
from typing import TypeVar, Dict, Generic
T = TypeVar('T')
S = TypeVar('S')
class D1(dict): pass # Implicit base class Dict[Any, Any]
D1([(1, 2)], x=1)
class D2(Dict[T, S], Generic[T, S]): pass
da = D2([('x', 2)], x=1)
da() # E: D2[str, int] not callable
D2([(1, 2)], x=1) # E: Keyword argument only valid with "str" key type in call to "dict"
db = D2(x=1)
db() # E: D2[str, int] not callable
[builtins fixtures/dict.py]

[case testSpecialSignatureForSubclassOfDict2]
from typing import TypeVar, Dict, Generic
T = TypeVar('T')
class D(Dict[str, T], Generic[T]): pass
D([('x', 1)], x=1)
[builtins fixtures/dict.py]

[case testOverridingSpecialSignatureInSubclassOfDict]
from typing import TypeVar, Dict, Generic
T = TypeVar('T')
S = TypeVar('S')
class D(Dict[T, S], Generic[T, S]):
def __init__(self, x: S, y: T) -> None: pass
d = D(1, y='')
d() # E: D[str, int] not callable
[builtins fixtures/dict.py]
[out]
main:1: error: Name 'undefined' is not defined
21 changes: 14 additions & 7 deletions mypy/test/data/fixtures/dict.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
# Builtins stub used in dictionary-related test cases.

from typing import TypeVar, Generic, Iterable, Iterator, Tuple
from typing import TypeVar, Generic, Iterable, Iterator, Tuple, overload

T = TypeVar('T')
S = TypeVar('S')
KT = TypeVar('KT')
VT = TypeVar('VT')

class object:
def __init__(self) -> None: pass

class type: pass

class dict(Iterable[T], Generic[T, S]):
def __init__(self, arg: Iterable[Tuple[T, S]] = None) -> None: pass
def __setitem__(self, k: T, v: S) -> None: pass
def __iter__(self) -> Iterator[T]: pass
def update(self, a: 'dict[T, S]') -> None: pass
class dict(Iterable[KT], Generic[KT, VT]):
@overload
def __init__(self, **kwargs: VT) -> None: pass
@overload
def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass
def __setitem__(self, k: KT, v: VT) -> None: pass
def __iter__(self) -> Iterator[KT]: pass
def update(self, a: 'dict[KT, VT]') -> None: pass

class int: pass # for convenience

class str: pass # for keyword argument key type

class list(Iterable[T], Generic[T]): # needed by some test cases
def __iter__(self) -> Iterator[T]: pass
def __mul__(self, x: int) -> list[T]: pass
Expand Down
9 changes: 8 additions & 1 deletion mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ class CallableType(FunctionLike):
is_classmethod_class = False
# Was this type implicitly generated instead of explicitly specified by the user?
implicit = False
# Defined for signatures that require special handling (currently only value is 'dict'
# for a signature similar to 'dict')
special_sig = None # type: Optional[str]

def __init__(self,
arg_types: List[Type],
Expand All @@ -419,6 +422,7 @@ def __init__(self,
is_ellipsis_args: bool = False,
implicit=False,
is_classmethod_class=False,
special_sig=None,
) -> None:
if variables is None:
variables = []
Expand All @@ -435,6 +439,7 @@ def __init__(self,
self.variables = variables
self.is_ellipsis_args = is_ellipsis_args
self.implicit = implicit
self.special_sig = special_sig
super().__init__(line)

def copy_modified(self,
Expand All @@ -447,7 +452,8 @@ def copy_modified(self,
definition: SymbolNode = _dummy,
variables: List[TypeVarDef] = _dummy,
line: int = _dummy,
is_ellipsis_args: bool = _dummy) -> 'CallableType':
is_ellipsis_args: bool = _dummy,
special_sig: Optional[str] = _dummy) -> 'CallableType':
return CallableType(
arg_types=arg_types if arg_types is not _dummy else self.arg_types,
arg_kinds=arg_kinds if arg_kinds is not _dummy else self.arg_kinds,
Expand All @@ -462,6 +468,7 @@ def copy_modified(self,
is_ellipsis_args if is_ellipsis_args is not _dummy else self.is_ellipsis_args),
implicit=self.implicit,
is_classmethod_class=self.is_classmethod_class,
special_sig=special_sig if special_sig is not _dummy else self.special_sig,
)

def is_type_obj(self) -> bool:
Expand Down