Skip to content

Add informative notes to invariant function arguments #3411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Aug 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4f6d386
Add notes to invariant argument error
quartox May 22, 2017
c12fc40
Fix type inference
quartox May 22, 2017
2b92465
Add invariant notes to tests
quartox May 22, 2017
05a8ee8
Remove stray comment
quartox May 22, 2017
1bf5a93
Split tests on ' # ' instead of '#'
quartox May 23, 2017
5a70c57
Move variance to new page
quartox May 23, 2017
b0536d5
Update doc url in invariant notes
quartox May 23, 2017
fa4093d
Revert to split on '#' for check tests
quartox May 23, 2017
2888c5f
Add variance to toc
quartox May 23, 2017
f8700c3
Move docs to correct files
quartox May 23, 2017
572160d
Switch order in toc
quartox May 23, 2017
900a927
Revert docs
quartox May 23, 2017
2fee5be
Improve test readability
quartox May 24, 2017
823e1c2
Merge branch 'master' into invariance_notes
quartox May 26, 2017
e22c4d4
Update documentation url
quartox May 26, 2017
add8811
Mapping is covariant in value only
quartox Aug 19, 2017
c31d6e4
Check type fullname
quartox Aug 19, 2017
b938394
Remove unused function
quartox Aug 19, 2017
fbdc381
Add Instance check and DRY
quartox Aug 19, 2017
c2810e3
Check for subtypes
quartox Aug 19, 2017
44a1846
Move Instance check to call site
quartox Aug 19, 2017
bffd7a4
Add more test cases
quartox Aug 19, 2017
7d18a99
Fix tests
quartox Aug 19, 2017
f17d6f6
Fix confusing names test
quartox Aug 19, 2017
35905de
Fix confusing names test and function length
quartox Aug 19, 2017
a9a02d4
Remove extra space between tests
quartox Aug 19, 2017
3599325
Remove extra space from end of line
quartox Aug 19, 2017
de2ede0
Add missing quotes
quartox Aug 19, 2017
bfe20dc
Check that arguments are Instance
quartox Aug 19, 2017
c225c59
Fix error message function name
quartox Aug 19, 2017
739e49f
Use is_same_type to check types
quartox Aug 19, 2017
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
33 changes: 33 additions & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type:
target = 'to {} '.format(name)

msg = ''
notes = [] # type: List[str]
if callee.name == '<list>':
name = callee.name[1:-1]
n -= 1
Expand Down Expand Up @@ -571,7 +572,12 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type:
arg_type_str = '**' + arg_type_str
msg = 'Argument {} {}has incompatible type {}; expected {}'.format(
n, target, arg_type_str, expected_type_str)
if isinstance(arg_type, Instance) and isinstance(expected_type, Instance):
notes = append_invariance_notes(notes, arg_type, expected_type)
self.fail(msg, context)
if notes:
for note_msg in notes:
self.note(note_msg, context)

def invalid_index_type(self, index_type: Type, expected_type: Type, base_str: str,
context: Context) -> None:
Expand Down Expand Up @@ -992,6 +998,33 @@ def pretty_or(args: List[str]) -> str:
return ", ".join(quoted[:-1]) + ", or " + quoted[-1]


def append_invariance_notes(notes: List[str], arg_type: Instance,
expected_type: Instance) -> List[str]:
"""Explain that the type is invariant and give notes for how to solve the issue."""
from mypy.subtypes import is_subtype
from mypy.sametypes import is_same_type
invariant_type = ''
covariant_suggestion = ''
if (arg_type.type.fullname() == 'builtins.list' and
expected_type.type.fullname() == 'builtins.list' and
is_subtype(arg_type.args[0], expected_type.args[0])):
invariant_type = 'List'
covariant_suggestion = 'Consider using "Sequence" instead, which is covariant'
elif (arg_type.type.fullname() == 'builtins.dict' and
expected_type.type.fullname() == 'builtins.dict' and
is_same_type(arg_type.args[0], expected_type.args[0]) and
is_subtype(arg_type.args[1], expected_type.args[1])):
invariant_type = 'Dict'
covariant_suggestion = ('Consider using "Mapping" instead, '
'which is covariant in the value type')
if invariant_type and covariant_suggestion:
notes.append(
'"{}" is invariant -- see '.format(invariant_type) +
'http://mypy.readthedocs.io/en/latest/common_issues.html#variance')
notes.append(covariant_suggestion)
return notes


def make_inferred_type_note(context: Context, subtype: Type,
supertype: Type, supertype_str: str) -> str:
"""Explain that the user may have forgotten to type a variable.
Expand Down
45 changes: 45 additions & 0 deletions test-data/unit/check-varargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -592,3 +592,48 @@ class C:
def foo(self) -> None: pass
C().foo()
C().foo(1) # The decorator's return type says this should be okay

[case testInvariantDictArgNote]
from typing import Dict, Sequence
def f(x: Dict[str, Sequence[int]]) -> None: pass
def g(x: Dict[str, float]) -> None: pass
def h(x: Dict[str, int]) -> None: pass
a = {'a': [1, 2]}
b = {'b': ['c', 'd']}
c = {'c': 1.0}
d = {'d': 1}
f(a) # E: Argument 1 to "f" has incompatible type Dict[str, List[int]]; expected Dict[str, Sequence[int]] \
# N: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
# N: Consider using "Mapping" instead, which is covariant in the value type
f(b) # E: Argument 1 to "f" has incompatible type Dict[str, List[str]]; expected Dict[str, Sequence[int]]
g(c)
g(d) # E: Argument 1 to "g" has incompatible type Dict[str, int]; expected Dict[str, float] \
# N: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
# N: Consider using "Mapping" instead, which is covariant in the value type
h(c) # E: Argument 1 to "h" has incompatible type Dict[str, float]; expected Dict[str, int]
h(d)
[builtins fixtures/dict.pyi]

[case testInvariantListArgNote]
from typing import List, Union
def f(numbers: List[Union[int, float]]) -> None: pass
a = [1, 2]
f(a) # E: Argument 1 to "f" has incompatible type List[int]; expected List[Union[int, float]] \
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
# N: Consider using "Sequence" instead, which is covariant
x = [1]
y = ['a']
x = y # E: Incompatible types in assignment (expression has type List[str], variable has type List[int])
[builtins fixtures/list.pyi]

[case testInvariantTypeConfusingNames]
from typing import TypeVar
class Listener: pass
class DictReader: pass
def f(x: Listener) -> None: pass
def g(y: DictReader) -> None: pass
a = [1, 2]
b = {'b': 1}
f(a) # E: Argument 1 to "f" has incompatible type List[int]; expected "Listener"
g(b) # E: Argument 1 to "g" has incompatible type Dict[str, int]; expected "DictReader"
[builtins fixtures/dict.pyi]