diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 01458c23..671e4691 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -15,9 +15,11 @@ Bug Fixes * Update convention support documentation (#386, #393) * Detect inner asynchronous functions for D202 (#467) +* Fix indentation error while parsing class methods (#441). * Fix a bug in parsing Google-style argument description. The bug caused some argument names to go unreported in D417 (#448). + 5.0.2 - January 8th, 2020 --------------------------- diff --git a/src/pydocstyle/checker.py b/src/pydocstyle/checker.py index ae608068..8e3be2c4 100644 --- a/src/pydocstyle/checker.py +++ b/src/pydocstyle/checker.py @@ -1007,9 +1007,20 @@ def is_def_arg_private(arg_name): """Return a boolean indicating if the argument name is private.""" return arg_name.startswith("_") -def get_function_args(function_string): +def get_function_args(function_source): """Return the function arguments given the source-code string.""" - function_arg_node = ast.parse(textwrap.dedent(function_string)).body[0].args + # We are stripping the whitespace from the left of the + # function source. + # This is so that if the docstring has incorrectly + # indented lines, which are at a lower indent than the + # function source, we still dedent the source correctly + # and the AST parser doesn't throw an error. + try: + function_arg_node = ast.parse(function_source.lstrip()).body[0].args + except SyntaxError: + # If we still get a syntax error, we don't want the + # the checker to crash. Instead we just return a blank list. + return [] arg_nodes = function_arg_node.args kwonly_arg_nodes = function_arg_node.kwonlyargs return [arg_node.arg for arg_node in chain(arg_nodes, kwonly_arg_nodes)] diff --git a/src/tests/test_cases/expected.py b/src/tests/test_cases/expected.py index fbe719a7..f131e16a 100644 --- a/src/tests/test_cases/expected.py +++ b/src/tests/test_cases/expected.py @@ -4,13 +4,13 @@ class Expectation: def __init__(self): self.expected = set() - def expect(self, *args, arg_count=0): + def expect(self, *args, arg_count=0, func_name=""): """Decorator that expects a certain PEP 257 violation.""" # The `arg_count` parameter helps the decorator # with functions that have positional arguments. if len(args) == 1: def decorate(f): - self.expected.add((f.__name__, args[0])) + self.expected.add((func_name or f.__name__, args[0])) f(*[None]*arg_count) return f return decorate diff --git a/src/tests/test_cases/sections.py b/src/tests/test_cases/sections.py index f8158ec7..2200ddfe 100644 --- a/src/tests/test_cases/sections.py +++ b/src/tests/test_cases/sections.py @@ -274,6 +274,25 @@ def missing_colon_google_style_section(): # noqa: D406, D407 """ +@expect("D417: Missing argument descriptions in the docstring " + "(argument(s) y are missing descriptions in " + "'bar' docstring)", func_name="bar") +def _test_nested_functions(): + x = 1 + + def bar(y=2): # noqa: D207, D213, D406, D407 + """Nested function test for docstrings. + + Will this work when referencing x? + + Args: + x: Test something +that is broken. + + """ + print(x) + + @expect(_D213) @expect("D417: Missing argument descriptions in the docstring " "(argument(s) y are missing descriptions in " @@ -370,13 +389,15 @@ def test_missing_docstring_another(skip, verbose): # noqa: D213, D407 @expect("D417: Missing argument descriptions in the docstring " "(argument(s) y are missing descriptions in " "'test_missing_numpy_args' docstring)") +@expect("D207: Docstring is under-indented") def test_missing_numpy_args(_private_arg=0, x=1, y=2): # noqa: D406, D407 """Toggle the gizmo. Parameters ---------- x : int - The greatest integer. + The greatest integer in the history \ +of the entire world. """ @@ -455,3 +476,20 @@ def test_mixing_numpy_and_google(danger): # noqa: D213 Zoneeeeee! """ + + +class TestIncorrectIndent: # noqa: D203 + """Test class.""" + + @expect("D417: Missing argument descriptions in the docstring " + "(argument(s) y are missing descriptions in " + "'test_incorrect_indent' docstring)", arg_count=3) + def test_incorrect_indent(self, x=1, y=2): # noqa: D207, D213, D407 + """Reproducing issue #437. + +Testing this incorrectly indented docstring. + + Args: + x: Test argument. + + """