Skip to content

Commit fedfc7f

Browse files
committed
feat: Support "Keyword Args" sections for G-style
Add support for "Keyword Args" and "Keyword Arguments" sections for Google-style docstrings. Closes #88.
1 parent 8e1b1b2 commit fedfc7f

File tree

2 files changed

+102
-22
lines changed

2 files changed

+102
-22
lines changed

src/pytkdocs/parsers/docstrings/google.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"arguments:": Section.Type.PARAMETERS,
1010
"params:": Section.Type.PARAMETERS,
1111
"parameters:": Section.Type.PARAMETERS,
12+
"keyword args:": Section.Type.PARAMETERS,
13+
"keyword arguments:": Section.Type.PARAMETERS,
1214
"raise:": Section.Type.EXCEPTIONS,
1315
"raises:": Section.Type.EXCEPTIONS,
1416
"except:": Section.Type.EXCEPTIONS,

tests/test_parsers/test_docstrings/test_google.py

Lines changed: 100 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ def test_sections_without_signature():
5252
nada: SEGFAULT.
5353
rien: SEGFAULT.
5454
55+
Keyword Args:
56+
keywd: SEGFAULT.
57+
5558
Exceptions:
5659
GlobalError: when nothing works as expected.
5760
@@ -60,8 +63,8 @@ def test_sections_without_signature():
6063
"""
6164
)
6265

63-
assert len(sections) == 4
64-
assert len(errors) == 5 # missing annotations for params and return
66+
assert len(sections) == 5
67+
assert len(errors) == 6 # missing annotations for params and return
6568
for error in errors[:-1]:
6669
assert "param" in error
6770
assert "return" in errors[-1]
@@ -79,43 +82,49 @@ def test_property_docstring():
7982
def test_function_without_annotations():
8083
"""Parse a function docstring without signature annotations."""
8184

82-
def f(x, y):
85+
def f(x, y, *, z):
8386
"""
8487
This function has no annotations.
8588
8689
Parameters:
8790
x: X value.
8891
y: Y value.
8992
93+
Keyword Args:
94+
z: Z value.
95+
9096
Returns:
91-
Sum X + Y.
97+
Sum X + Y + Z.
9298
"""
93-
return x + y
99+
return x + y + z
94100

95101
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
96-
assert len(sections) == 3
102+
assert len(sections) == 4
97103
assert len(errors) == 1
98104
assert "No type in return" in errors[0]
99105

100106

101107
def test_function_with_annotations():
102108
"""Parse a function docstring with signature annotations."""
103109

104-
def f(x: int, y: int) -> int:
110+
def f(x: int, y: int, *, z: int) -> int:
105111
"""
106112
This function has annotations.
107113
108114
Parameters:
109115
x: X value.
110116
y: Y value.
111117
118+
Keyword Arguments:
119+
z: Z value.
120+
112121
Returns:
113122
Sum X + Y.
114123
"""
115124
return x + y
116125

117126
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
118-
assert len(sections) == 3
127+
assert len(sections) == 4
119128
assert not errors
120129

121130

@@ -188,25 +197,29 @@ def f(x: int, y: int) -> int:
188197
def test_types_in_docstring():
189198
"""Parse types in docstring."""
190199

191-
def f(x, y):
200+
def f(x, y, *, z):
192201
"""
193202
The types are written in the docstring.
194203
195204
Parameters:
196205
x (int): X value.
197206
y (int): Y value.
198207
208+
Keyword Args:
209+
z (int): Z value.
210+
199211
Returns:
200-
int: Sum X + Y.
212+
int: Sum X + Y + Z.
201213
"""
202-
return x + y
214+
return x + y + z
203215

204216
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
205-
assert len(sections) == 3
217+
assert len(sections) == 4
206218
assert not errors
207219

208220
x, y = sections[1].value
209-
r = sections[2].value
221+
(z,) = sections[2].value
222+
r = sections[3].value
210223

211224
assert x.name == "x"
212225
assert x.annotation == "int"
@@ -220,31 +233,41 @@ def f(x, y):
220233
assert y.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
221234
assert y.default is inspect.Signature.empty
222235

236+
assert z.name == "z"
237+
assert z.annotation == "int"
238+
assert z.description == "Z value."
239+
assert z.kind is inspect.Parameter.KEYWORD_ONLY
240+
assert z.default is inspect.Signature.empty
241+
223242
assert r.annotation == "int"
224-
assert r.description == "Sum X + Y."
243+
assert r.description == "Sum X + Y + Z."
225244

226245

227246
def test_types_and_optional_in_docstring():
228247
"""Parse optional types in docstring."""
229248

230-
def f(x=1, y=None):
249+
def f(x=1, y=None, *, z=None):
231250
"""
232251
The types are written in the docstring.
233252
234253
Parameters:
235254
x (int): X value.
236255
y (int, optional): Y value.
237256
257+
Keyword Args:
258+
z (int, optional): Z value.
259+
238260
Returns:
239-
int: Sum X + Y.
261+
int: Sum X + Y + Z.
240262
"""
241-
return x + (y or 1)
263+
return x + (y or 1) + (z or 1)
242264

243265
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
244-
assert len(sections) == 3
266+
assert len(sections) == 4
245267
assert not errors
246268

247269
x, y = sections[1].value
270+
(z,) = sections[2].value
248271

249272
assert x.name == "x"
250273
assert x.annotation == "int"
@@ -258,25 +281,34 @@ def f(x=1, y=None):
258281
assert y.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
259282
assert y.default is None
260283

284+
assert z.name == "z"
285+
assert z.annotation == "int"
286+
assert z.description == "Z value."
287+
assert z.kind is inspect.Parameter.KEYWORD_ONLY
288+
assert z.default is None
289+
261290

262291
def test_types_in_signature_and_docstring():
263292
"""Parse types in both signature and docstring."""
264293

265-
def f(x: int, y: int) -> int:
294+
def f(x: int, y: int, *, z: int) -> int:
266295
"""
267296
The types are written both in the signature and in the docstring.
268297
269298
Parameters:
270299
x (int): X value.
271300
y (int): Y value.
272301
302+
Keyword Args:
303+
z (int): Z value.
304+
273305
Returns:
274-
int: Sum X + Y.
306+
int: Sum X + Y + Z.
275307
"""
276-
return x + y
308+
return x + y + z
277309

278310
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
279-
assert len(sections) == 3
311+
assert len(sections) == 4
280312
assert not errors
281313

282314

@@ -401,6 +433,23 @@ def f(x: int):
401433
assert "Empty" in errors[1]
402434

403435

436+
def test_param_line_without_colon_keyword_only():
437+
"""Warn when missing colon."""
438+
439+
def f(*, x: int):
440+
"""
441+
Keyword Args:
442+
x is an integer.
443+
"""
444+
return x
445+
446+
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
447+
assert not sections # getting x fails, so the section is empty and discarded
448+
assert len(errors) == 2
449+
assert "pair" in errors[0]
450+
assert "Empty" in errors[1]
451+
452+
404453
def test_admonitions():
405454
"""Parse admonitions."""
406455

@@ -493,6 +542,35 @@ def f(a, *args, **kwargs):
493542
assert not errors
494543

495544

545+
def test_parse_args_kwargs_keyword_only():
546+
"""Parse args and kwargs."""
547+
548+
def f(a, *args, **kwargs):
549+
"""
550+
Arguments:
551+
a: a parameter.
552+
*args: args parameters.
553+
554+
Keyword Args:
555+
**kwargs: kwargs parameters.
556+
"""
557+
return 1
558+
559+
sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
560+
assert len(sections) == 2
561+
expected_parameters = {"a": "a parameter.", "*args": "args parameters."}
562+
for param in sections[0].value:
563+
assert param.name in expected_parameters
564+
assert expected_parameters[param.name] == param.description
565+
566+
expected_parameters = {"**kwargs": "kwargs parameters."}
567+
for param in sections[1].value:
568+
assert param.name in expected_parameters
569+
assert expected_parameters[param.name] == param.description
570+
571+
assert not errors
572+
573+
496574
def test_different_indentation():
497575
"""Parse different indentations, warn on confusing indentation."""
498576

0 commit comments

Comments
 (0)