Skip to content

Commit a9cc3ca

Browse files
committed
docstring_parser: pass in the line number to the hook for use with errors.
Result line numbers should now be absolute rather than relative.
1 parent 37e6685 commit a9cc3ca

File tree

3 files changed

+52
-31
lines changed

3 files changed

+52
-31
lines changed

mypy/fastparse.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,16 @@ def pop_and_convert(name: str) -> Optional[Type]:
127127
if t is None:
128128
return AnyType()
129129
else:
130-
return parse_type_comment(t[0], line + t[1], errors)
130+
return parse_type_comment(t[0], t[1], errors)
131131

132132
if hook.func is not None:
133-
type_map = options.hooks.docstring_parser.func(docstring, hook.options, errors)
133+
type_map = options.hooks.docstring_parser.func(docstring, line, hook.options, errors)
134134
if type_map:
135135
arg_types = [pop_and_convert(name) for name in arg_names]
136136
return_type = pop_and_convert('return')
137137
if type_map:
138138
errors.report(line, 0,
139-
TYPE_COMMENT_DOCSTRING_ERROR.format(type_map.keys(), arg_names))
139+
TYPE_COMMENT_DOCSTRING_ERROR.format(list(type_map), arg_names))
140140
return arg_types, return_type
141141
return None
142142

mypy/hooks.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,22 @@ def __repr__(self) -> str:
8989

9090

9191
# The docstring_parser hook is called for each unannotated function that has a
92-
# docstring. The callable should accept three arguments:
92+
# docstring. The callable should accept four arguments:
9393
# - the docstring to be parsed
94+
# - the line number where the docstring begins
9495
# - a dictionary of options parsed from the [docstring_parser] section of mypy
9596
# config file
9697
# - an Errors object for reporting errors, warnings, and info.
9798
#
9899
# The function should return a map from argument name to 2-tuple. The tuple should contain:
99100
# 1) a PEP484-compatible string to use as the argument's type
100-
# 2) a line number offset, relative to the start of the docstring, identifying the location
101-
# of the type annotation within the docstring. This is used to improve errors if the
102-
# type string is invalid.
101+
# 2) a line number identifying the location of the type annotation within the docstring.
102+
# This is used to improve errors if the type string is invalid.
103103
#
104104
# The function's return type, if specified, is stored in the mapping with the special
105105
# key 'return'. Other than 'return', each key of the mapping must be one of the
106106
# arguments of the documented function; otherwise, an error will be raised.
107-
DocstringParserHook = Hook[Callable[[str, HookOptions, 'Errors'], Dict[str, Tuple[str, int]]]]
107+
DocstringParserHook = Hook[Callable[[str, int, HookOptions, 'Errors'], Dict[str, Tuple[str, int]]]]
108108

109109

110110
class Hooks:

test-data/unit/check-docstring-hook.test

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ f('') # E: Argument 1 to "f" has incompatible type "str"; expected "int"
1111
[[mypy]
1212
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
1313
[file myhooks.py]
14-
def parse_docstring(docstring, opts, errs):
14+
def parse_docstring(docstring, line, opts, errs):
1515
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
16-
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
16+
return {k.strip(): (v.strip(), line + i + 1) for i, (k, v) in enumerate(params)}
1717

1818
[case testMethodDocstringHook]
1919
# flags: --config-file tmp/mypy.ini
@@ -30,44 +30,65 @@ A().f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "
3030
[[mypy]
3131
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
3232
[file myhooks.py]
33-
def parse_docstring(docstring, opts, errs):
33+
def parse_docstring(docstring, line, opts, errs):
3434
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
35-
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
35+
return {k.strip(): (v.strip(), line + i + 1) for i, (k, v) in enumerate(params)}
3636

3737
[case testSparseDocstringAnnotations]
3838
# flags: --config-file tmp/mypy.ini
3939
def f(x, y):
40-
"""
41-
x: int
42-
"""
40+
"""trigger parsing"""
4341
return 1
4442
f('', 1) # E: Argument 1 to "f" has incompatible type "str"; expected "int"
4543
[file mypy.ini]
4644
[[mypy]
4745
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
4846
[file myhooks.py]
49-
def parse_docstring(docstring, opts, errs):
50-
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
51-
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
52-
47+
def parse_docstring(docstring, line, opts, errs):
48+
return {'x': ('int', line)}
5349

5450
[case testInvalidDocstringAnnotation]
5551
# flags: --config-file tmp/mypy.ini
5652
def f(x):
57-
"""
58-
x: B/A/D
59-
return: None
60-
"""
53+
"""trigger parsing"""
6154
return None
6255
[file mypy.ini]
6356
[[mypy]
6457
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
6558
[file myhooks.py]
66-
def parse_docstring(docstring, opts, errs):
67-
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
68-
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
59+
def parse_docstring(docstring, line, opts, errs):
60+
return {'x': ('B/A/D', line), 'return': ('None', line)}
61+
[out]
62+
main:2: error: invalid type comment or annotation
63+
64+
[case testDocstringHookErrors]
65+
# flags: --config-file tmp/mypy.ini
66+
def f(x):
67+
"""trigger parsing"""
68+
return ''
69+
[file mypy.ini]
70+
[[mypy]
71+
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
72+
[file myhooks.py]
73+
def parse_docstring(docstring, line, opts, errs):
74+
errs.report(line, 0, 'custom error')
75+
return {}
76+
[out]
77+
main:2: error: custom error
78+
79+
[case testDocstringMismatchErrors]
80+
# flags: --config-file tmp/mypy.ini
81+
def f(x):
82+
"""trigger parsing"""
83+
return ''
84+
[file mypy.ini]
85+
[[mypy]
86+
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
87+
[file myhooks.py]
88+
def parse_docstring(docstring, line, opts, errs):
89+
return {'x': ('int', line), 'y': ('int', line)}
6990
[out]
70-
main:3: error: invalid type comment or annotation
91+
main:2: error: Arguments parsed from docstring are not present in function signature: ['y'] not in ['x']
7192

7293
-- Python 2.7
7394
-- -------------------------
@@ -85,9 +106,9 @@ f('') # E: Argument 1 to "f" has incompatible type "str"; expected "int"
85106
[[mypy]
86107
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
87108
[file myhooks.py]
88-
def parse_docstring(docstring, opts, errs):
109+
def parse_docstring(docstring, line, opts, errs):
89110
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
90-
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
111+
return {k.strip(): (v.strip(), line + i + 1) for i, (k, v) in enumerate(params)}
91112

92113
[case testMethodDocstringHook27]
93114
# flags: --config-file tmp/mypy.ini --python-version 2.7
@@ -104,7 +125,7 @@ A().f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "
104125
[[mypy]
105126
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
106127
[file myhooks.py]
107-
def parse_docstring(docstring, opts, errs):
128+
def parse_docstring(docstring, line, opts, errs):
108129
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
109-
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
130+
return {k.strip(): (v.strip(), line + i + 1) for i, (k, v) in enumerate(params)}
110131

0 commit comments

Comments
 (0)