Skip to content

Commit

Permalink
docstring_parser: pass in the line number to the hook for use with er…
Browse files Browse the repository at this point in the history
…rors.

Result line numbers should now be absolute rather than relative.
  • Loading branch information
chadrik committed Jun 5, 2017
1 parent 37e6685 commit a9cc3ca
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 31 deletions.
6 changes: 3 additions & 3 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,16 @@ def pop_and_convert(name: str) -> Optional[Type]:
if t is None:
return AnyType()
else:
return parse_type_comment(t[0], line + t[1], errors)
return parse_type_comment(t[0], t[1], errors)

if hook.func is not None:
type_map = options.hooks.docstring_parser.func(docstring, hook.options, errors)
type_map = options.hooks.docstring_parser.func(docstring, line, hook.options, errors)
if type_map:
arg_types = [pop_and_convert(name) for name in arg_names]
return_type = pop_and_convert('return')
if type_map:
errors.report(line, 0,
TYPE_COMMENT_DOCSTRING_ERROR.format(type_map.keys(), arg_names))
TYPE_COMMENT_DOCSTRING_ERROR.format(list(type_map), arg_names))
return arg_types, return_type
return None

Expand Down
10 changes: 5 additions & 5 deletions mypy/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,22 @@ def __repr__(self) -> str:


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


class Hooks:
Expand Down
67 changes: 44 additions & 23 deletions test-data/unit/check-docstring-hook.test
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ f('') # E: Argument 1 to "f" has incompatible type "str"; expected "int"
[[mypy]
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
[file myhooks.py]
def parse_docstring(docstring, opts, errs):
def parse_docstring(docstring, line, opts, errs):
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
return {k.strip(): (v.strip(), line + i + 1) for i, (k, v) in enumerate(params)}

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

[case testSparseDocstringAnnotations]
# flags: --config-file tmp/mypy.ini
def f(x, y):
"""
x: int
"""
"""trigger parsing"""
return 1
f('', 1) # E: Argument 1 to "f" has incompatible type "str"; expected "int"
[file mypy.ini]
[[mypy]
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
[file myhooks.py]
def parse_docstring(docstring, opts, errs):
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}

def parse_docstring(docstring, line, opts, errs):
return {'x': ('int', line)}

[case testInvalidDocstringAnnotation]
# flags: --config-file tmp/mypy.ini
def f(x):
"""
x: B/A/D
return: None
"""
"""trigger parsing"""
return None
[file mypy.ini]
[[mypy]
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
[file myhooks.py]
def parse_docstring(docstring, opts, errs):
params = [l.split(':', 1) for l in docstring.strip().split('\n')]
return {k.strip(): (v.strip(), i + 1) for i, (k, v) in enumerate(params)}
def parse_docstring(docstring, line, opts, errs):
return {'x': ('B/A/D', line), 'return': ('None', line)}
[out]
main:2: error: invalid type comment or annotation

[case testDocstringHookErrors]
# flags: --config-file tmp/mypy.ini
def f(x):
"""trigger parsing"""
return ''
[file mypy.ini]
[[mypy]
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
[file myhooks.py]
def parse_docstring(docstring, line, opts, errs):
errs.report(line, 0, 'custom error')
return {}
[out]
main:2: error: custom error

[case testDocstringMismatchErrors]
# flags: --config-file tmp/mypy.ini
def f(x):
"""trigger parsing"""
return ''
[file mypy.ini]
[[mypy]
hooks.docstring_parser = tmp/myhooks.py:parse_docstring
[file myhooks.py]
def parse_docstring(docstring, line, opts, errs):
return {'x': ('int', line), 'y': ('int', line)}
[out]
main:3: error: invalid type comment or annotation
main:2: error: Arguments parsed from docstring are not present in function signature: ['y'] not in ['x']

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

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

0 comments on commit a9cc3ca

Please sign in to comment.