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

Skip to content

Commit a202b42

Browse files
rafaelcaricioilevkivskyi
authored andcommitted
Improve message when an annotation is expected (#6782)
Fixes #2463 Improve error message when partial types are found for built-in types `Dict`, `List`, `Set`, `FrozenSet`. Messages used to be like: ``` main:3: error: Need type annotation for 'x' ``` This PR modifies it to look like: ``` main:3: error: Need type annotation for 'x' (hint: "x: List[<type>] = ...") ``` In case the variable is not of a built-in type the hints are not shown.
1 parent c4a6e40 commit a202b42

14 files changed

Lines changed: 74 additions & 49 deletions

mypy/checker.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2496,13 +2496,13 @@ def infer_variable_type(self, name: Var, lvalue: Lvalue,
24962496
# partial type which will be made more specific later. A partial type
24972497
# gets generated in assignment like 'x = []' where item type is not known.
24982498
if not self.infer_partial_type(name, lvalue, init_type):
2499-
self.msg.need_annotation_for_var(name, context)
2499+
self.msg.need_annotation_for_var(name, context, self.options.python_version)
25002500
self.set_inference_error_fallback_type(name, lvalue, init_type, context)
25012501
elif (isinstance(lvalue, MemberExpr) and self.inferred_attribute_types is not None
25022502
and lvalue.def_var and lvalue.def_var in self.inferred_attribute_types
25032503
and not is_same_type(self.inferred_attribute_types[lvalue.def_var], init_type)):
25042504
# Multiple, inconsistent types inferred for an attribute.
2505-
self.msg.need_annotation_for_var(name, context)
2505+
self.msg.need_annotation_for_var(name, context, self.options.python_version)
25062506
name.type = AnyType(TypeOfAny.from_error)
25072507
else:
25082508
# Infer type of the target.
@@ -3724,7 +3724,7 @@ def enter_partial_types(self, *, is_function: bool = False,
37243724
var.type = NoneType()
37253725
else:
37263726
if var not in self.partial_reported and not permissive:
3727-
self.msg.need_annotation_for_var(var, context)
3727+
self.msg.need_annotation_for_var(var, context, self.options.python_version)
37283728
self.partial_reported.add(var)
37293729
if var.type:
37303730
var.type = self.fixup_partial_type(var.type)
@@ -3748,7 +3748,8 @@ def handle_partial_var_type(
37483748
if in_scope:
37493749
context = partial_types[node]
37503750
if is_local or not self.options.allow_untyped_globals:
3751-
self.msg.need_annotation_for_var(node, context)
3751+
self.msg.need_annotation_for_var(node, context,
3752+
self.options.python_version)
37523753
else:
37533754
# Defer the node -- we might get a better type in the outer scope
37543755
self.handle_cannot_determine_type(node.name(), context)

mypy/messages.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
2121
from mypy.types import (
2222
Type, CallableType, Instance, TypeVarType, TupleType, TypedDictType, LiteralType,
2323
UnionType, NoneType, AnyType, Overloaded, FunctionLike, DeletedType, TypeType,
24-
UninhabitedType, TypeOfAny, ForwardRef, UnboundType
24+
UninhabitedType, TypeOfAny, ForwardRef, UnboundType, PartialType
2525
)
2626
from mypy.nodes import (
2727
TypeInfo, Context, MypyFile, op_methods, FuncDef, reverse_builtin_aliases,
2828
ARG_POS, ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2,
2929
ReturnStmt, NameExpr, Var, CONTRAVARIANT, COVARIANT, SymbolNode,
3030
CallExpr
3131
)
32+
from mypy.defaults import PYTHON3_VERSION
3233
from mypy.util import unmangle
3334
from mypy import message_registry
3435

@@ -1077,8 +1078,22 @@ def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> N
10771078
self.fail("{} becomes {} due to an unfollowed import".format(prefix, self.format(typ)),
10781079
ctx)
10791080

1080-
def need_annotation_for_var(self, node: SymbolNode, context: Context) -> None:
1081-
self.fail("Need type annotation for '{}'".format(unmangle(node.name())), context)
1081+
def need_annotation_for_var(self, node: SymbolNode, context: Context,
1082+
python_version: Optional[Tuple[int, int]] = None) -> None:
1083+
hint = ''
1084+
# Only gives hint if it's a variable declaration and the partial type is a builtin type
1085+
if (python_version and isinstance(node, Var) and isinstance(node.type, PartialType) and
1086+
node.type.type and node.type.type.fullname() in reverse_builtin_aliases):
1087+
alias = reverse_builtin_aliases[node.type.type.fullname()]
1088+
alias = alias.split('.')[-1]
1089+
type_dec = '<type>'
1090+
if alias == 'Dict':
1091+
type_dec = '{}, {}'.format(type_dec, type_dec)
1092+
if python_version < (3, 6):
1093+
hint = ' (hint: "{} = ... # type: {}[{}]")'.format(node.name(), alias, type_dec)
1094+
else:
1095+
hint = ' (hint: "{}: {}[{}] = ...")'.format(node.name(), alias, type_dec)
1096+
self.fail("Need type annotation for '{}'{}".format(unmangle(node.name()), hint), context)
10821097

10831098
def explicit_any(self, ctx: Context) -> None:
10841099
self.fail('Explicit "Any" is not allowed', ctx)

mypy/test/data.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,12 +513,14 @@ def expand_errors(input: List[str], output: List[str], fnam: str) -> None:
513513
elif m.group(1) == 'W':
514514
severity = 'warning'
515515
col = m.group('col')
516+
message = m.group('message')
517+
message = message.replace('\\#', '#') # adds back escaped # character
516518
if col is None:
517519
output.append(
518-
'{}:{}: {}: {}'.format(fnam, i + 1, severity, m.group('message')))
520+
'{}:{}: {}: {}'.format(fnam, i + 1, severity, message))
519521
else:
520522
output.append('{}:{}:{}: {}: {}'.format(
521-
fnam, i + 1, col, severity, m.group('message')))
523+
fnam, i + 1, col, severity, message))
522524

523525

524526
def fix_win_path(line: str) -> str:

runtests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
STUBGEN_PY]
3535

3636
# We split the pytest run into three parts to improve test
37-
# parallelization. Each run should have tests that each take a roughly similiar
37+
# parallelization. Each run should have tests that each take a roughly similar
3838
# time to run.
3939
cmds = {
4040
# Self type check

test-data/unit/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Add the test in this format anywhere in the file:
3030
with text "abc..."
3131
- note a space after `E:` and `flags:`
3232
- `# E:12` adds column number to the expected error
33+
- use `\` to escape the `#` character and indicate that the rest of the line is part of
34+
the error message
3335
- repeating `# E: ` several times in one line indicates multiple expected errors in one line
3436
- `W: ...` and `N: ...` works exactly like `E:`, but report a warning and a note respectively
3537
- lines that don't contain the above should cause no type check errors

test-data/unit/check-classes.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ class C:
912912
x = C.x
913913
[builtins fixtures/list.pyi]
914914
[out]
915-
main:2: error: Need type annotation for 'x'
915+
main:2: error: Need type annotation for 'x' (hint: "x: List[<type>] = ...")
916916

917917
[case testAccessingGenericClassAttribute]
918918
from typing import Generic, TypeVar

test-data/unit/check-expressions.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,7 @@ d5 = dict(a=1, b='') # type: Dict[str, Any]
17141714

17151715
[case testDictWithoutKeywordArgs]
17161716
from typing import Dict
1717-
d = dict() # E: Need type annotation for 'd'
1717+
d = dict() # E: Need type annotation for 'd' (hint: "d: Dict[<type>, <type>] = ...")
17181718
d2 = dict() # type: Dict[int, str]
17191719
dict(undefined) # E: Name 'undefined' is not defined
17201720
[builtins fixtures/dict.pyi]
@@ -1953,8 +1953,8 @@ a.__pow__() # E: Too few arguments for "__pow__" of "int"
19531953
[builtins fixtures/ops.pyi]
19541954

19551955
[case testTypeAnnotationNeededMultipleAssignment]
1956-
x, y = [], [] # E: Need type annotation for 'x' \
1957-
# E: Need type annotation for 'y'
1956+
x, y = [], [] # E: Need type annotation for 'x' (hint: "x: List[<type>] = ...") \
1957+
# E: Need type annotation for 'y' (hint: "y: List[<type>] = ...")
19581958
[builtins fixtures/list.pyi]
19591959

19601960
[case testStrictEqualityEq]

test-data/unit/check-functions.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,7 +1514,7 @@ if g(C()):
15141514
def f(x: B) -> B: pass
15151515

15161516
[case testRedefineFunctionDefinedAsVariableInitializedToEmptyList]
1517-
f = [] # E: Need type annotation for 'f'
1517+
f = [] # E: Need type annotation for 'f' (hint: "f: List[<type>] = ...")
15181518
if object():
15191519
def f(): pass # E: Incompatible redefinition
15201520
f() # E: "List[Any]" not callable
@@ -2320,7 +2320,7 @@ def make_list() -> List[T]: pass
23202320

23212321
l: List[int] = make_list()
23222322

2323-
bad = make_list() # E: Need type annotation for 'bad'
2323+
bad = make_list() # E: Need type annotation for 'bad' (hint: "bad: List[<type>] = ...")
23242324
[builtins fixtures/list.pyi]
23252325

23262326
[case testAnonymousArgumentError]

test-data/unit/check-inference-context.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ class B(A): pass
425425
[case testLocalVariableInferenceFromEmptyList]
426426
import typing
427427
def f() -> None:
428-
a = [] # E: Need type annotation for 'a'
428+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
429429
b = [None]
430430
c = [B()]
431431
if int():

test-data/unit/check-inference.test

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,20 +1375,20 @@ a.append(0) # E: Argument 1 to "append" of "list" has incompatible type "int";
13751375
[out]
13761376

13771377
[case testInferListInitializedToEmptyAndNotAnnotated]
1378-
a = [] # E: Need type annotation for 'a'
1378+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
13791379
[builtins fixtures/list.pyi]
13801380
[out]
13811381

13821382
[case testInferListInitializedToEmptyAndReadBeforeAppend]
1383-
a = [] # E: Need type annotation for 'a'
1383+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
13841384
if a: pass
13851385
a.xyz # E: "List[Any]" has no attribute "xyz"
13861386
a.append('')
13871387
[builtins fixtures/list.pyi]
13881388
[out]
13891389

13901390
[case testInferListInitializedToEmptyAndIncompleteTypeInAppend]
1391-
a = [] # E: Need type annotation for 'a'
1391+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
13921392
a.append([])
13931393
a() # E: "List[Any]" not callable
13941394
[builtins fixtures/list.pyi]
@@ -1413,7 +1413,7 @@ def f() -> None:
14131413

14141414
[case testInferListInitializedToEmptyAndNotAnnotatedInFunction]
14151415
def f() -> None:
1416-
a = [] # E: Need type annotation for 'a'
1416+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
14171417

14181418
def g() -> None: pass
14191419

@@ -1424,7 +1424,7 @@ a.append(1)
14241424

14251425
[case testInferListInitializedToEmptyAndReadBeforeAppendInFunction]
14261426
def f() -> None:
1427-
a = [] # E: Need type annotation for 'a'
1427+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
14281428
if a: pass
14291429
a.xyz # E: "List[Any]" has no attribute "xyz"
14301430
a.append('')
@@ -1441,7 +1441,7 @@ class A:
14411441

14421442
[case testInferListInitializedToEmptyAndNotAnnotatedInClassBody]
14431443
class A:
1444-
a = [] # E: Need type annotation for 'a'
1444+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
14451445

14461446
class B:
14471447
a = []
@@ -1461,15 +1461,15 @@ class A:
14611461
[case testInferListInitializedToEmptyAndNotAnnotatedInMethod]
14621462
class A:
14631463
def f(self) -> None:
1464-
a = [] # E: Need type annotation for 'a'
1464+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
14651465
[builtins fixtures/list.pyi]
14661466
[out]
14671467

14681468
[case testInferListInitializedToEmptyInMethodViaAttribute]
14691469
class A:
14701470
def f(self) -> None:
14711471
# Attributes aren't supported right now.
1472-
self.a = [] # E: Need type annotation for 'a'
1472+
self.a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
14731473
self.a.append(1)
14741474
self.a.append('')
14751475
[builtins fixtures/list.pyi]
@@ -1480,7 +1480,7 @@ from typing import List
14801480

14811481
class A:
14821482
def __init__(self) -> None:
1483-
self.x = [] # E: Need type annotation for 'x'
1483+
self.x = [] # E: Need type annotation for 'x' (hint: "x: List[<type>] = ...")
14841484

14851485
class B(A):
14861486
# TODO?: This error is kind of a false positive, unfortunately
@@ -1526,16 +1526,16 @@ a() # E: "Dict[str, int]" not callable
15261526
[out]
15271527

15281528
[case testInferDictInitializedToEmptyUsingUpdateError]
1529-
a = {} # E: Need type annotation for 'a'
1529+
a = {} # E: Need type annotation for 'a' (hint: "a: Dict[<type>, <type>] = ...")
15301530
a.update([1, 2]) # E: Argument 1 to "update" of "dict" has incompatible type "List[int]"; expected "Mapping[Any, Any]"
15311531
a() # E: "Dict[Any, Any]" not callable
15321532
[builtins fixtures/dict.pyi]
15331533
[out]
15341534

15351535
[case testInferDictInitializedToEmptyAndIncompleteTypeInUpdate]
1536-
a = {} # E: Need type annotation for 'a'
1536+
a = {} # E: Need type annotation for 'a' (hint: "a: Dict[<type>, <type>] = ...")
15371537
a[1] = {}
1538-
b = {} # E: Need type annotation for 'b'
1538+
b = {} # E: Need type annotation for 'b' (hint: "b: Dict[<type>, <type>] = ...")
15391539
b[{}] = 1
15401540
[builtins fixtures/dict.pyi]
15411541
[out]
@@ -1555,14 +1555,14 @@ def add():
15551555
[case testSpecialCaseEmptyListInitialization]
15561556
def f(blocks: Any): # E: Name 'Any' is not defined \
15571557
# N: Did you forget to import it from "typing"? (Suggestion: "from typing import Any")
1558-
to_process = [] # E: Need type annotation for 'to_process'
1558+
to_process = [] # E: Need type annotation for 'to_process' (hint: "to_process: List[<type>] = ...")
15591559
to_process = list(blocks)
15601560
[builtins fixtures/list.pyi]
15611561
[out]
15621562

15631563
[case testSpecialCaseEmptyListInitialization2]
15641564
def f(blocks: object):
1565-
to_process = [] # E: Need type annotation for 'to_process'
1565+
to_process = [] # E: Need type annotation for 'to_process' (hint: "to_process: List[<type>] = ...")
15661566
to_process = list(blocks) # E: No overload variant of "list" matches argument type "object" \
15671567
# N: Possible overload variant: \
15681568
# N: def [T] __init__(self, x: Iterable[T]) -> List[T] \
@@ -1621,7 +1621,7 @@ x.append('') # E: Argument 1 to "append" of "list" has incompatible type "str";
16211621
x = None
16221622
if object():
16231623
# Promote from partial None to partial list.
1624-
x = [] # E: Need type annotation for 'x'
1624+
x = [] # E: Need type annotation for 'x' (hint: "x: List[<type>] = ...")
16251625
x
16261626
[builtins fixtures/list.pyi]
16271627

@@ -1630,7 +1630,7 @@ def f() -> None:
16301630
x = None
16311631
if object():
16321632
# Promote from partial None to partial list.
1633-
x = [] # E: Need type annotation for 'x'
1633+
x = [] # E: Need type annotation for 'x' (hint: "x: List[<type>] = ...")
16341634
[builtins fixtures/list.pyi]
16351635
[out]
16361636

@@ -1716,7 +1716,7 @@ class A:
17161716
pass
17171717
[builtins fixtures/for.pyi]
17181718
[out]
1719-
main:3: error: Need type annotation for 'x'
1719+
main:3: error: Need type annotation for 'x' (hint: "x: List[<type>] = ...")
17201720

17211721
[case testPartialTypeErrorSpecialCase3]
17221722
class A:
@@ -1882,9 +1882,9 @@ o = 1
18821882

18831883
[case testMultipassAndPartialTypesSpecialCase3]
18841884
def f() -> None:
1885-
x = {} # E: Need type annotation for 'x'
1885+
x = {} # E: Need type annotation for 'x' (hint: "x: Dict[<type>, <type>] = ...")
18861886
y = o
1887-
z = {} # E: Need type annotation for 'z'
1887+
z = {} # E: Need type annotation for 'z' (hint: "z: Dict[<type>, <type>] = ...")
18881888
o = 1
18891889
[builtins fixtures/dict.pyi]
18901890
[out]
@@ -2070,7 +2070,7 @@ main:4: error: Invalid type: try using Literal[0] instead?
20702070
class C:
20712071
x = None
20722072
def __init__(self) -> None:
2073-
self.x = [] # E: Need type annotation for 'x'
2073+
self.x = [] # E: Need type annotation for 'x' (hint: "x: List[<type>] = ...")
20742074
[builtins fixtures/list.pyi]
20752075
[out]
20762076

@@ -2246,7 +2246,7 @@ class A:
22462246
[case testLocalPartialTypesWithClassAttributeInitializedToEmptyDict]
22472247
# flags: --local-partial-types
22482248
class A:
2249-
x = {} # E: Need type annotation for 'x'
2249+
x = {} # E: Need type annotation for 'x' (hint: "x: Dict[<type>, <type>] = ...")
22502250

22512251
def f(self) -> None:
22522252
self.x[0] = ''
@@ -2269,7 +2269,7 @@ reveal_type(a) # E: Revealed type is 'builtins.list[builtins.int]'
22692269

22702270
[case testLocalPartialTypesWithGlobalInitializedToEmptyList2]
22712271
# flags: --local-partial-types
2272-
a = [] # E: Need type annotation for 'a'
2272+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
22732273

22742274
def f() -> None:
22752275
a.append(1)
@@ -2280,7 +2280,7 @@ reveal_type(a) # E: Revealed type is 'builtins.list[Any]'
22802280

22812281
[case testLocalPartialTypesWithGlobalInitializedToEmptyList3]
22822282
# flags: --local-partial-types
2283-
a = [] # E: Need type annotation for 'a'
2283+
a = [] # E: Need type annotation for 'a' (hint: "a: List[<type>] = ...")
22842284

22852285
def f():
22862286
a.append(1)
@@ -2302,7 +2302,7 @@ reveal_type(a) # E: Revealed type is 'builtins.dict[builtins.int, builtins.str]'
23022302

23032303
[case testLocalPartialTypesWithGlobalInitializedToEmptyDict2]
23042304
# flags: --local-partial-types
2305-
a = {} # E: Need type annotation for 'a'
2305+
a = {} # E: Need type annotation for 'a' (hint: "a: Dict[<type>, <type>] = ...")
23062306

23072307
def f() -> None:
23082308
a[0] = ''
@@ -2313,7 +2313,7 @@ reveal_type(a) # E: Revealed type is 'builtins.dict[Any, Any]'
23132313

23142314
[case testLocalPartialTypesWithGlobalInitializedToEmptyDict3]
23152315
# flags: --local-partial-types
2316-
a = {} # E: Need type annotation for 'a'
2316+
a = {} # E: Need type annotation for 'a' (hint: "a: Dict[<type>, <type>] = ...")
23172317

23182318
def f():
23192319
a[0] = ''

0 commit comments

Comments
 (0)