Skip to content
This repository was archived by the owner on Nov 3, 2023. It is now read-only.

Commit eb8d312

Browse files
committed
Fix indentation error while parsing class methods
In order to determine the missing arguments, we currently use ast.parse to parse partial source code. This parsing might lead to syntax errors. We catch the syntax error and make the parsing more resilient to errors in the source code. Fixes #437
1 parent ebc5466 commit eb8d312

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

docs/release_notes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Bug Fixes
1515

1616
* Update convention support documentation (#386, #393)
1717
* Detect inner asynchronous functions for D202 (#467)
18+
* Fix indentation error while parsing class methods (#441).
19+
1820

1921
5.0.2 - January 8th, 2020
2022
---------------------------

src/pydocstyle/checker.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,9 +1007,20 @@ def is_def_arg_private(arg_name):
10071007
"""Return a boolean indicating if the argument name is private."""
10081008
return arg_name.startswith("_")
10091009

1010-
def get_function_args(function_string):
1010+
def get_function_args(function_source):
10111011
"""Return the function arguments given the source-code string."""
1012-
function_arg_node = ast.parse(textwrap.dedent(function_string)).body[0].args
1012+
# We are stripping the whitespace from the left of the
1013+
# function source.
1014+
# This is so that if the docstring has incorrectly
1015+
# indented lines, which are at a lower indent than the
1016+
# function source, we still dedent the source correctly
1017+
# and the AST parser doesn't throw an error.
1018+
try:
1019+
function_arg_node = ast.parse(function_source.lstrip()).body[0].args
1020+
except SyntaxError:
1021+
# If we still get a syntax error, we don't want the
1022+
# the checker to crash. Instead we just return a blank list.
1023+
return []
10131024
arg_nodes = function_arg_node.args
10141025
kwonly_arg_nodes = function_arg_node.kwonlyargs
10151026
return [arg_node.arg for arg_node in chain(arg_nodes, kwonly_arg_nodes)]

src/tests/test_cases/expected.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ class Expectation:
44
def __init__(self):
55
self.expected = set()
66

7-
def expect(self, *args, arg_count=0):
7+
def expect(self, *args, arg_count=0, func_name=""):
88
"""Decorator that expects a certain PEP 257 violation."""
99
# The `arg_count` parameter helps the decorator
1010
# with functions that have positional arguments.
1111
if len(args) == 1:
1212
def decorate(f):
13-
self.expected.add((f.__name__, args[0]))
13+
self.expected.add((func_name or f.__name__, args[0]))
1414
f(*[None]*arg_count)
1515
return f
1616
return decorate

src/tests/test_cases/sections.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,25 @@ def missing_colon_google_style_section(): # noqa: D406, D407
274274
"""
275275

276276

277+
@expect("D417: Missing argument descriptions in the docstring "
278+
"(argument(s) y are missing descriptions in "
279+
"'bar' docstring)", func_name="bar")
280+
def _test_nested_functions():
281+
x = 1
282+
283+
def bar(y=2): # noqa: D207, D213, D406, D407
284+
"""Nested function test for docstrings.
285+
286+
Will this work when referencing x?
287+
288+
Args:
289+
x: Test something
290+
that is broken.
291+
292+
"""
293+
print(x)
294+
295+
277296
@expect(_D213)
278297
@expect("D417: Missing argument descriptions in the docstring "
279298
"(argument(s) y are missing descriptions in "
@@ -340,13 +359,15 @@ def test_missing_args_static_method(a, x, y, _test, z=3): # noqa: D213, D407
340359
@expect("D417: Missing argument descriptions in the docstring "
341360
"(argument(s) y are missing descriptions in "
342361
"'test_missing_numpy_args' docstring)")
362+
@expect("D207: Docstring is under-indented")
343363
def test_missing_numpy_args(_private_arg=0, x=1, y=2): # noqa: D406, D407
344364
"""Toggle the gizmo.
345365
346366
Parameters
347367
----------
348368
x : int
349-
The greatest integer.
369+
The greatest integer in the history \
370+
of the entire world.
350371
351372
"""
352373

@@ -425,3 +446,20 @@ def test_mixing_numpy_and_google(danger): # noqa: D213
425446
Zoneeeeee!
426447
427448
"""
449+
450+
451+
class TestIncorrectIndent: # noqa: D203
452+
"""Test class."""
453+
454+
@expect("D417: Missing argument descriptions in the docstring "
455+
"(argument(s) y are missing descriptions in "
456+
"'test_incorrect_indent' docstring)", arg_count=3)
457+
def test_incorrect_indent(self, x=1, y=2): # noqa: D207, D213, D407
458+
"""Reproducing issue #437.
459+
460+
Testing this incorrectly indented docstring.
461+
462+
Args:
463+
x: Test argument.
464+
465+
"""

0 commit comments

Comments
 (0)