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

Commit ecbc0ea

Browse files
sambhavNurdok
authored andcommitted
Extend support for detecting missing arguments in Numpy style docstrings (#407)
* Remove Parameters from valid Google section names Google docstrings should document their arguments in the Args section not the Parameters section. See http://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings * Add support for checking missing parameters in Numpy docstrings Fixes #394 This adds support for checking missing arguments in Numpy docstrings. * Clarify D417 error about missing descriptions and add tests for it D417 is raised even if that parameter is present in the docstring, but has an empty description. * Fix tests
1 parent c9feef2 commit ecbc0ea

File tree

4 files changed

+153
-16
lines changed

4 files changed

+153
-16
lines changed

docs/release_notes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ New Features
1515

1616
* Extend support for detecting missing arguments in Google style
1717
docstrings to method calls (#384).
18+
* Extend support for detecting missing argument description in Numpy style
19+
docstrings (#407).
20+
1821

1922
Bug Fixes
2023

src/pydocstyle/checker.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ class ConventionChecker:
7272
'Methods',
7373
'Note',
7474
'Notes',
75-
'Other Parameters',
76-
'Parameters',
7775
'Return',
7876
'Returns',
7977
'Raises',
@@ -678,6 +676,48 @@ def _check_numpy_section(cls, docstring, definition, context):
678676
if suffix:
679677
yield violations.D406(capitalized_section, context.line.strip())
680678

679+
if capitalized_section == "Parameters":
680+
yield from cls._check_parameters_section(docstring, definition, context)
681+
682+
@staticmethod
683+
def _check_parameters_section(docstring, definition, context):
684+
"""D417: `Parameters` section check for numpy style.
685+
686+
Check for a valid `Parameters` section. Checks that:
687+
* The section documents all function arguments (D417)
688+
except `self` or `cls` if it is a method.
689+
690+
"""
691+
docstring_args = set()
692+
section_level_indent = leading_space(context.line)
693+
content = context.following_lines
694+
for current_line, next_line in zip(content, content[1:]):
695+
# All parameter definitions in the Numpy parameters
696+
# section must be at the same indent level as the section
697+
# name.
698+
# Also, we ensure that the following line is indented,
699+
# and has some string, to ensure that the parameter actually
700+
# has a description.
701+
# This means, this is a parameter doc with some description
702+
if ((leading_space(current_line) == section_level_indent)
703+
and (len(leading_space(next_line)) > len(leading_space(current_line)))
704+
and next_line.strip()):
705+
# In case the parameter has type definitions, it
706+
# will have a colon
707+
if ":" in current_line:
708+
parameters, parameter_type = current_line.split(":", 1)
709+
# Else, we simply have the list of parameters defined
710+
# on the current line.
711+
else:
712+
parameters = current_line.strip()
713+
# Numpy allows grouping of multiple parameters of same
714+
# type in the same line. They are comma separated.
715+
parameter_list = parameters.split(",")
716+
for parameter in parameter_list:
717+
docstring_args.add(parameter.strip())
718+
yield from ConventionChecker._check_missing_args(docstring_args, definition)
719+
720+
681721
@staticmethod
682722
def _check_args_section(docstring, definition, context):
683723
"""D417: `Args` section checks.
@@ -692,6 +732,19 @@ def _check_args_section(docstring, definition, context):
692732
match = ConventionChecker.GOOGLE_ARGS_REGEX.match(line)
693733
if match:
694734
docstring_args.add(match.group(1))
735+
yield from ConventionChecker._check_missing_args(docstring_args, definition)
736+
737+
738+
@staticmethod
739+
def _check_missing_args(docstring_args, definition):
740+
"""D417: Yield error for missing arguments in docstring.
741+
742+
Given a list of arguments found in the docstring and the
743+
callable definition, it checks if all the arguments of the
744+
callable are present in the docstring, else it yields a
745+
D417 with a list of missing arguments.
746+
747+
"""
695748
function_args = get_function_args(definition.source)
696749
# If the method isn't static, then we skip the first
697750
# positional argument as it is `cls` or `self`

src/pydocstyle/violations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ def to_rst(cls) -> str:
252252
'mark, or exclamation point', 'not {0!r}')
253253
D416 = D4xx.create_error('D416', 'Section name should end with a colon',
254254
'{0!r}, not {1!r}')
255-
D417 = D4xx.create_error('D417', 'Missing arguments in the docstring',
256-
'argument(s) {0!r} missing in {1!r} docstring')
255+
D417 = D4xx.create_error('D417', 'Missing argument descriptions in the docstring',
256+
'argument(s) {0} are missing descriptions in {1!r} docstring')
257257

258258
class AttrDict(dict):
259259
def __getattr__(self, item: str) -> Any:

src/tests/test_cases/sections.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,10 @@ def missing_colon_google_style_section(): # noqa: D406, D407
275275

276276

277277
@expect(_D213)
278-
@expect("D417: Missing arguments in the docstring "
279-
"(argument(s) 'y' missing in "
280-
"'test_missing_args' docstring)")
281-
def test_missing_args(x=1, y=2): # noqa: D406, D407
278+
@expect("D417: Missing argument descriptions in the docstring "
279+
"(argument(s) y are missing descriptions in "
280+
"'test_missing_google_args' docstring)")
281+
def test_missing_google_args(x=1, y=2): # noqa: D406, D407
282282
"""Toggle the gizmo.
283283
284284
Args:
@@ -287,7 +287,7 @@ def test_missing_args(x=1, y=2): # noqa: D406, D407
287287
"""
288288

289289

290-
class Test: # noqa: D203
290+
class TestGoogle: # noqa: D203
291291
"""Test class."""
292292

293293
def test_method(self, test, another_test): # noqa: D213, D407
@@ -299,8 +299,8 @@ def test_method(self, test, another_test): # noqa: D213, D407
299299
300300
"""
301301

302-
@expect("D417: Missing arguments in the docstring "
303-
"(argument(s) 'test, y, z' missing in "
302+
@expect("D417: Missing argument descriptions in the docstring "
303+
"(argument(s) test, y, z are missing descriptions in "
304304
"'test_missing_args' docstring)", arg_count=4)
305305
def test_missing_args(self, test, x, y, z=3): # noqa: D213, D407
306306
"""Test a valid args section.
@@ -311,20 +311,21 @@ def test_missing_args(self, test, x, y, z=3): # noqa: D213, D407
311311
"""
312312

313313
@classmethod
314-
@expect("D417: Missing arguments in the docstring "
315-
"(argument(s) 'test, y, z' missing in "
314+
@expect("D417: Missing argument descriptions in the docstring "
315+
"(argument(s) test, y, z are missing descriptions in "
316316
"'test_missing_args_class_method' docstring)", arg_count=4)
317317
def test_missing_args_class_method(cls, test, x, y, z=3): # noqa: D213, D407
318318
"""Test a valid args section.
319319
320320
Args:
321-
x: Another parameter.
321+
x: Another parameter. The parameter below is missing description.
322+
y:
322323
323324
"""
324325

325326
@staticmethod
326-
@expect("D417: Missing arguments in the docstring "
327-
"(argument(s) 'a, y, z' missing in "
327+
@expect("D417: Missing argument descriptions in the docstring "
328+
"(argument(s) a, y, z are missing descriptions in "
328329
"'test_missing_args_static_method' docstring)", arg_count=3)
329330
def test_missing_args_static_method(a, x, y, z=3): # noqa: D213, D407
330331
"""Test a valid args section.
@@ -333,3 +334,83 @@ def test_missing_args_static_method(a, x, y, z=3): # noqa: D213, D407
333334
x: Another parameter.
334335
335336
"""
337+
338+
339+
@expect(_D213)
340+
@expect("D417: Missing argument descriptions in the docstring "
341+
"(argument(s) y are missing descriptions in "
342+
"'test_missing_numpy_args' docstring)")
343+
def test_missing_numpy_args(x=1, y=2): # noqa: D406, D407
344+
"""Toggle the gizmo.
345+
346+
Parameters
347+
----------
348+
x : int
349+
The greatest integer.
350+
351+
"""
352+
353+
354+
class TestNumpy: # noqa: D203
355+
"""Test class."""
356+
357+
def test_method(self, test, another_test, x=1, y=2): # noqa: D213, D407
358+
"""Test a valid args section.
359+
360+
Parameters
361+
----------
362+
test, another_test
363+
Some parameters without type.
364+
x, y : int
365+
Some integer parameters.
366+
367+
"""
368+
369+
@expect("D417: Missing argument descriptions in the docstring "
370+
"(argument(s) test, y, z are missing descriptions in "
371+
"'test_missing_args' docstring)", arg_count=4)
372+
def test_missing_args(self, test, x, y, z=3, t=1): # noqa: D213, D407
373+
"""Test a valid args section.
374+
375+
Parameters
376+
----------
377+
x, t : int
378+
Some parameters.
379+
380+
381+
"""
382+
383+
@classmethod
384+
@expect("D417: Missing argument descriptions in the docstring "
385+
"(argument(s) test, y, z are missing descriptions in "
386+
"'test_missing_args_class_method' docstring)", arg_count=4)
387+
def test_missing_args_class_method(cls, test, x, y, z=3): # noqa: D213, D407
388+
"""Test a valid args section.
389+
390+
Parameters
391+
----------
392+
z
393+
x
394+
Another parameter. The parameters y, test below are
395+
missing descriptions. The parameter z above is also missing
396+
a description.
397+
y
398+
test
399+
400+
"""
401+
402+
@staticmethod
403+
@expect("D417: Missing argument descriptions in the docstring "
404+
"(argument(s) a, z are missing descriptions in "
405+
"'test_missing_args_static_method' docstring)", arg_count=3)
406+
def test_missing_args_static_method(a, x, y, z=3, t=1): # noqa: D213, D407
407+
"""Test a valid args section.
408+
409+
Parameters
410+
----------
411+
x, y
412+
Another parameter.
413+
t : int
414+
Yet another parameter.
415+
416+
"""

0 commit comments

Comments
 (0)