Skip to content

Commit 3d1323e

Browse files
committed
Improve error message for partial Nones
When --local-partial-types is set and we can't infer a complete type for a type that we initially inferred as partial None, show an error message that suggests to add a type annotation of the form Optional[<type>].
1 parent 8e7e817 commit 3d1323e

File tree

2 files changed

+22
-14
lines changed

2 files changed

+22
-14
lines changed

mypy/messages.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,18 +1193,26 @@ def need_annotation_for_var(self, node: SymbolNode, context: Context,
11931193
python_version: Optional[Tuple[int, int]] = None) -> None:
11941194
hint = ''
11951195
has_variable_annotations = not python_version or python_version >= (3, 6)
1196+
# type to recommend the user adds
1197+
recommended_type = None
11961198
# Only gives hint if it's a variable declaration and the partial type is a builtin type
1197-
if (python_version and isinstance(node, Var) and isinstance(node.type, PartialType) and
1198-
node.type.type and node.type.type.fullname in reverse_builtin_aliases):
1199-
alias = reverse_builtin_aliases[node.type.type.fullname]
1200-
alias = alias.split('.')[-1]
1199+
if python_version and isinstance(node, Var) and isinstance(node.type, PartialType):
12011200
type_dec = '<type>'
1202-
if alias == 'Dict':
1203-
type_dec = f'{type_dec}, {type_dec}'
1201+
if not node.type.type:
1202+
# partial None
1203+
recommended_type = f'Optional[{type_dec}]'
1204+
elif node.type.type.fullname in reverse_builtin_aliases:
1205+
# partial types other than partial None
1206+
alias = reverse_builtin_aliases[node.type.type.fullname]
1207+
alias = alias.split('.')[-1]
1208+
if alias == 'Dict':
1209+
type_dec = f'{type_dec}, {type_dec}'
1210+
recommended_type = f'{alias}[{type_dec}]'
1211+
if recommended_type is not None:
12041212
if has_variable_annotations:
1205-
hint = f' (hint: "{node.name}: {alias}[{type_dec}] = ...")'
1213+
hint = f' (hint: "{node.name}: {recommended_type} = ...")'
12061214
else:
1207-
hint = f' (hint: "{node.name} = ... # type: {alias}[{type_dec}]")'
1215+
hint = f' (hint: "{node.name} = ... # type: {recommended_type}")'
12081216

12091217
if has_variable_annotations:
12101218
needed = 'annotation'

test-data/unit/check-inference.test

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,7 +2394,7 @@ if bool():
23942394

23952395
[case testLocalPartialTypesWithGlobalInitializedToNone]
23962396
# flags: --local-partial-types
2397-
x = None # E: Need type annotation for "x"
2397+
x = None # E: Need type annotation for "x" (hint: "x: Optional[<type>] = ...")
23982398

23992399
def f() -> None:
24002400
global x
@@ -2405,7 +2405,7 @@ reveal_type(x) # N: Revealed type is "None"
24052405

24062406
[case testLocalPartialTypesWithGlobalInitializedToNone2]
24072407
# flags: --local-partial-types
2408-
x = None # E: Need type annotation for "x"
2408+
x = None # E: Need type annotation for "x" (hint: "x: Optional[<type>] = ...")
24092409

24102410
def f():
24112411
global x
@@ -2454,7 +2454,7 @@ reveal_type(a) # N: Revealed type is "builtins.str"
24542454
[case testLocalPartialTypesWithClassAttributeInitializedToNone]
24552455
# flags: --local-partial-types
24562456
class A:
2457-
x = None # E: Need type annotation for "x"
2457+
x = None # E: Need type annotation for "x" (hint: "x: Optional[<type>] = ...")
24582458

24592459
def f(self) -> None:
24602460
self.x = 1
@@ -2637,7 +2637,7 @@ from typing import List
26372637
def f(x): pass
26382638

26392639
class A:
2640-
x = None # E: Need type annotation for "x"
2640+
x = None # E: Need type annotation for "x" (hint: "x: Optional[<type>] = ...")
26412641

26422642
def f(self, p: List[str]) -> None:
26432643
self.x = f(p)
@@ -2647,15 +2647,15 @@ class A:
26472647
[case testLocalPartialTypesAccessPartialNoneAttribute]
26482648
# flags: --local-partial-types
26492649
class C:
2650-
a = None # E: Need type annotation for "a"
2650+
a = None # E: Need type annotation for "a" (hint: "a: Optional[<type>] = ...")
26512651

26522652
def f(self, x) -> None:
26532653
C.a.y # E: Item "None" of "Optional[Any]" has no attribute "y"
26542654

26552655
[case testLocalPartialTypesAccessPartialNoneAttribute2]
26562656
# flags: --local-partial-types
26572657
class C:
2658-
a = None # E: Need type annotation for "a"
2658+
a = None # E: Need type annotation for "a" (hint: "a: Optional[<type>] = ...")
26592659

26602660
def f(self, x) -> None:
26612661
self.a.y # E: Item "None" of "Optional[Any]" has no attribute "y"

0 commit comments

Comments
 (0)