Skip to content

Commit 2d03d48

Browse files
committed
rephrase message; test tuple parameter; use call to check
1 parent e85b17d commit 2d03d48

12 files changed

+137
-25
lines changed

mypy/checker.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -740,9 +740,7 @@ def is_implicit_any(t: Type) -> bool:
740740

741741
# Type check initialization expressions.
742742
for arg in item.arguments:
743-
init = arg.initialization_statement
744-
if init:
745-
self.accept(init)
743+
self.expr_checker.check_default_arg(arg)
746744

747745
# Type check body in a new scope.
748746
with self.binder.top_frame_context():

mypy/checkexpr.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from collections import OrderedDict
44
from typing import cast, Dict, Set, List, Tuple, Callable, Union, Optional
55

6-
from mypy.errors import report_internal_error
6+
from mypy.errors import Errors, report_internal_error
77
from mypy.typeanal import has_any_from_unimported_type, check_for_explicit_any, set_any_tvars
88
from mypy.types import (
99
Type, AnyType, CallableType, Overloaded, NoneTyp, TypeVarDef,
@@ -1011,6 +1011,20 @@ def check_arg(self, caller_type: Type, original_caller_type: Type,
10111011
messages.incompatible_argument(n, m, callee, original_caller_type,
10121012
caller_kind, context)
10131013

1014+
def check_default_arg(self, arg: mypy.nodes.Argument) -> None:
1015+
rvalue = arg.initializer
1016+
lval_type = arg.variable.type
1017+
if rvalue and lval_type and not (self.chk.is_stub and isinstance(rvalue, EllipsisExpr)):
1018+
single = CallableType([lval_type], [ARG_POS], [None],
1019+
AnyType(), self.named_type('builtins.function'))
1020+
msg = MessageBuilder(Errors(), self.chk.modules)
1021+
self.check_call(single, [rvalue], [ARG_POS], arg, arg_messages=msg)
1022+
if msg.is_errors():
1023+
rval_type = self.accept(rvalue, lval_type)
1024+
varname = arg.variable.name()
1025+
self.msg.incompatible_default_argument(
1026+
varname, lval_type, rval_type, arg)
1027+
10141028
def overload_call_target(self, arg_types: List[Type], arg_kinds: List[int],
10151029
arg_names: List[str],
10161030
overload: Overloaded, context: Context,

mypy/fastparse.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def is_no_type_check_decorator(expr: ast3.expr) -> bool:
139139
return False
140140

141141

142-
class ASTConverter(ast3.NodeTransformer): # type: ignore # typeshed PR #931
142+
class ASTConverter(ast3.NodeTransformer):
143143
def __init__(self,
144144
options: Options,
145145
is_stub: bool,
@@ -962,7 +962,7 @@ def visit_Index(self, n: ast3.Index) -> Node:
962962
return self.visit(n.value)
963963

964964

965-
class TypeConverter(ast3.NodeTransformer): # type: ignore # typeshed PR #931
965+
class TypeConverter(ast3.NodeTransformer):
966966
def __init__(self, errors: Errors, line: int = -1) -> None:
967967
self.errors = errors
968968
self.line = line

mypy/fastparse2.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,9 +377,7 @@ def transform_args(self,
377377
n: ast27.arguments,
378378
line: int,
379379
) -> Tuple[List[Argument], List[Statement]]:
380-
# TODO: remove the cast once https://github.com/python/typeshed/pull/522
381-
# is accepted and synced
382-
type_comments = cast(List[str], n.type_comments) # type: ignore
380+
type_comments = n.type_comments
383381
converter = TypeConverter(self.errors, line=line)
384382
decompose_stmts = [] # type: List[Statement]
385383

@@ -826,7 +824,7 @@ def visit_Call(self, n: ast27.Call) -> CallExpr:
826824
return CallExpr(self.visit(n.func),
827825
self.translate_expr_list(arg_types),
828826
arg_kinds,
829-
cast("List[str]", signature))
827+
signature)
830828

831829
# Num(object n) -- a number as a PyObject.
832830
@with_line

mypy/messages.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,16 @@ def untyped_function_call(self, callee: CallableType, context: Context) -> Type:
492492
self.fail('Call to untyped function {} in typed context'.format(name), context)
493493
return AnyType()
494494

495+
def incompatible_default_argument(self, name: str, lvalue: Type, rvalue: Type,
496+
context: Context) -> None:
497+
if name.startswith('__tuple_arg_'):
498+
fmt = "Incompatible default for tuple argument no' {}".format(name[12:])
499+
else:
500+
fmt = 'Incompatible default for argument "{}"'.format(name)
501+
fmt += ' (argument has type {}, default has type {})'
502+
lvalt, rvalt = self.format_distinctly(lvalue, rvalue)
503+
self.fail(fmt.format(lvalt, rvalt), context)
504+
495505
def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: Type,
496506
arg_kind: int, context: Context) -> None:
497507
"""Report an error about an incompatible argument type.

mypy/semanal.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,7 +1377,23 @@ def visit_import_from(self, imp: ImportFrom) -> None:
13771377
self.add_submodules_to_parent_modules(possible_module_id, True)
13781378
elif possible_module_id in self.missing_modules:
13791379
missing = True
1380-
1380+
# If it is still not resolved, and the module is a stub
1381+
# check for a module level __getattr__
1382+
if module and not node and module.is_stub and '__getattr__' in module.names:
1383+
getattr_defn = module.names['__getattr__']
1384+
if isinstance(getattr_defn.node, FuncDef):
1385+
if isinstance(getattr_defn.node.type, CallableType):
1386+
typ = getattr_defn.node.type.ret_type
1387+
else:
1388+
typ = AnyType()
1389+
if as_id:
1390+
name = as_id
1391+
else:
1392+
name = id
1393+
ast_node = Var(name, type=typ)
1394+
symbol = SymbolTableNode(GDEF, ast_node, name)
1395+
self.add_symbol(name, symbol, imp)
1396+
return
13811397
if node and node.kind != UNBOUND_IMPORTED:
13821398
node = self.normalize_type_alias(node, imp)
13831399
if not node:
@@ -4278,8 +4294,7 @@ def consider_sys_version_info(expr: Expression, pyversion: Tuple[int, ...]) -> i
42784294
else:
42794295
return TRUTH_VALUE_UNKNOWN
42804296
elif isinstance(index, tuple) and isinstance(thing, tuple):
4281-
# Why doesn't mypy see that index can't be None here?
4282-
lo, hi = cast(Tuple[Optional[int], Optional[int]], index)
4297+
lo, hi = index
42834298
if lo is None:
42844299
lo = 0
42854300
if hi is None:

mypy/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1762,7 +1762,7 @@ def function_type(func: mypy.nodes.FuncBase, fallback: Instance) -> FunctionLike
17621762
# Implicit type signature with dynamic types.
17631763
# Overloaded functions always have a signature, so func must be an ordinary function.
17641764
assert isinstance(func, mypy.nodes.FuncItem), str(func)
1765-
return callable_type(cast(mypy.nodes.FuncItem, func), fallback)
1765+
return callable_type(func, fallback)
17661766

17671767

17681768
def callable_type(fdef: mypy.nodes.FuncItem, fallback: Instance,

mypy_self_check.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ warn_no_return = True
55
strict_optional = True
66
no_implicit_optional = True
77
disallow_any = generics, unimported
8+
warn_redundant_casts = True
9+
warn_unused_ignores = True
810

911
; historical exceptions
1012
[mypy-mypy.binder]

test-data/unit/check-functions.test

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,17 +388,42 @@ class A: pass
388388

389389
[case testDefaultArgumentExpressions2]
390390
import typing
391-
def f(x: 'A' = B()) -> None: # E: Incompatible types in assignment (expression has type "B", variable has type "A")
391+
def f(x: 'A' = B()) -> None: # E: Incompatible default for argument "x" (argument has type "A", default has type "B")
392392
b = x # type: B # E: Incompatible types in assignment (expression has type "A", variable has type "B")
393393
a = x # type: A
394394

395395
class B: pass
396396
class A: pass
397-
[out]
397+
398+
[case testDefaultArgumentExpressionsGeneric]
399+
from typing import TypeVar
400+
T = TypeVar('T', bound='A')
401+
def f(x: T = B()) -> None: # E: Incompatible default for argument "x" (argument has type "T", default has type "B")
402+
b = x # type: B # E: Incompatible types in assignment (expression has type "T", variable has type "B")
403+
a = x # type: A
404+
405+
class B: pass
406+
class A: pass
407+
408+
[case testDefaultArgumentExpressionsPython2]
409+
# flags: --python-version 2.7
410+
from typing import Tuple
411+
def f(x = B()): # E: Incompatible default for argument "x" (argument has type "A", default has type "B")
412+
# type: (A) -> None
413+
b = x # type: B # E: Incompatible types in assignment (expression has type "A", variable has type "B")
414+
a = x # type: A
415+
416+
def g((x, y) = (A(), B())): # E: Incompatible default for tuple argument no' 1 (argument has type "Tuple[B, B]", default has type "Tuple[A, B]")
417+
# type: ( Tuple[B, B] ) -> None
418+
b = x # type: B
419+
a = x # type: A # E: Incompatible types in assignment (expression has type "B", variable has type "A")
420+
421+
class B: pass
422+
class A: pass
398423

399424
[case testDefaultArgumentsWithSubtypes]
400425
import typing
401-
def f(x: 'B' = A()) -> None: # E: Incompatible types in assignment (expression has type "A", variable has type "B")
426+
def f(x: 'B' = A()) -> None: # E: Incompatible default for argument "x" (argument has type "B", default has type "A")
402427
pass
403428
def g(x: 'A' = B()) -> None:
404429
pass
@@ -409,7 +434,7 @@ class B(A): pass
409434

410435
[case testMultipleDefaultArgumentExpressions]
411436
import typing
412-
def f(x: 'A' = B(), y: 'B' = B()) -> None: # E: Incompatible types in assignment (expression has type "B", variable has type "A")
437+
def f(x: 'A' = B(), y: 'B' = B()) -> None: # E: Incompatible default for argument "x" (argument has type "A", default has type "B")
413438
pass
414439
def h(x: 'A' = A(), y: 'B' = B()) -> None:
415440
pass
@@ -420,7 +445,7 @@ class B: pass
420445

421446
[case testMultipleDefaultArgumentExpressions2]
422447
import typing
423-
def g(x: 'A' = A(), y: 'B' = A()) -> None: # E: Incompatible types in assignment (expression has type "A", variable has type "B")
448+
def g(x: 'A' = A(), y: 'B' = A()) -> None: # E: Incompatible default for argument "y" (argument has type "B", default has type "A")
424449
pass
425450

426451
class A: pass

test-data/unit/check-inference.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ from typing import Callable
11191119
def f(a: Callable[..., None] = lambda *a, **k: None):
11201120
pass
11211121

1122-
def g(a: Callable[..., None] = lambda *a, **k: 1): # E: Incompatible types in assignment (expression has type Callable[[VarArg(Any), KwArg(Any)], int], variable has type Callable[..., None])
1122+
def g(a: Callable[..., None] = lambda *a, **k: 1): # E: Incompatible default for argument "a" (argument has type Callable[..., None], default has type Callable[[VarArg(Any), KwArg(Any)], int])
11231123
pass
11241124
[builtins fixtures/dict.pyi]
11251125

test-data/unit/check-modules.test

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -475,11 +475,11 @@ def f(x: int = ...) -> None: pass
475475
[file m.pyi]
476476
def g(x: int = '') -> None: pass
477477
[out]
478-
tmp/m.pyi:1: error: Incompatible types in assignment (expression has type "str", variable has type "int")
479-
main:2: error: Incompatible types in assignment (expression has type "ellipsis", variable has type "int")
478+
tmp/m.pyi:1: error: Incompatible default for argument "x" (argument has type "int", default has type "str")
479+
main:2: error: Incompatible default for argument "x" (argument has type "int", default has type "ellipsis")
480480

481481
[case testEllipsisDefaultArgValueInNonStub]
482-
def f(x: int = ...) -> None: pass # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "int")
482+
def f(x: int = ...) -> None: pass # E: Incompatible default for argument "x" (argument has type "int", default has type "ellipsis")
483483
[out]
484484

485485
[case testStarImportOverlapping]
@@ -1766,3 +1766,53 @@ main:3: error: Module has no attribute "any_attribute"
17661766
[case testModuleLevelGetattribute]
17671767

17681768
def __getattribute__(): ... # E: __getattribute__ is not valid at the module level
1769+
1770+
[case testModuleLevelGetattrImportFrom]
1771+
from has_attr import name
1772+
reveal_type(name) # E: Revealed type is 'Any'
1773+
1774+
[file has_attr.pyi]
1775+
from typing import Any
1776+
def __getattr__(name: str) -> Any: ...
1777+
1778+
[builtins fixtures/module.pyi]
1779+
1780+
[case testModuleLevelGetattrImportFromRetType]
1781+
from has_attr import int_attr
1782+
reveal_type(int_attr) # E: Revealed type is 'builtins.int'
1783+
1784+
[file has_attr.pyi]
1785+
def __getattr__(name: str) -> int: ...
1786+
1787+
[builtins fixtures/module.pyi]
1788+
1789+
[case testModuleLevelGetattrImportFromNotStub]
1790+
from non_stub import name
1791+
reveal_type(name)
1792+
1793+
[file non_stub.py]
1794+
from typing import Any
1795+
def __getattr__(name: str) -> Any: ...
1796+
1797+
[out]
1798+
tmp/non_stub.py:2: error: __getattr__ is not valid at the module level outside a stub file
1799+
main:1: error: Module 'non_stub' has no attribute 'name'
1800+
main:2: error: Revealed type is 'Any'
1801+
1802+
[builtins fixtures/module.pyi]
1803+
1804+
[case testModuleLevelGetattrImportFromAs]
1805+
from has_attr import name as n
1806+
reveal_type(name)
1807+
reveal_type(n)
1808+
1809+
[file has_attr.pyi]
1810+
from typing import Any
1811+
def __getattr__(name: str) -> Any: ...
1812+
1813+
[out]
1814+
main:2: error: Revealed type is 'Any'
1815+
main:2: error: Name 'name' is not defined
1816+
main:3: error: Revealed type is 'Any'
1817+
1818+
[builtins fixtures/module.pyi]

test-data/unit/check-optional.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ f(None)
127127

128128
[case testNoInferOptionalFromDefaultNone]
129129
# flags: --no-implicit-optional
130-
def f(x: int = None) -> None: # E: Incompatible types in assignment (expression has type None, variable has type "int")
130+
def f(x: int = None) -> None: # E: Incompatible default for argument "x" (argument has type "int", default has type None)
131131
pass
132132
[out]
133133

@@ -140,7 +140,7 @@ f(None)
140140

141141
[case testNoInferOptionalFromDefaultNoneComment]
142142
# flags: --no-implicit-optional
143-
def f(x=None): # E: Incompatible types in assignment (expression has type None, variable has type "int")
143+
def f(x=None): # E: Incompatible default for argument "x" (argument has type "int", default has type None)
144144
# type: (int) -> None
145145
pass
146146
[out]

0 commit comments

Comments
 (0)