Skip to content

Commit b4d8897

Browse files
gh-104683: Argument Clinic: Refactor and simplify 'add docstring' states (#107550)
Introduce docstring_append() helper, and use it for both parameter and function docstrings. Remove docstring fixup from do_post_block_processing_cleanup(); instead, make sure no fixup is needed. Co-authored-by: Alex Waygood <[email protected]>
1 parent 818c83c commit b4d8897

File tree

2 files changed

+45
-32
lines changed

2 files changed

+45
-32
lines changed

Lib/test/test_clinic.py

+22
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,28 @@ def test_function_docstring(self):
648648
Path to be examined
649649
""")
650650

651+
def test_docstring_trailing_whitespace(self):
652+
function = self.parse_function(
653+
"module t\n"
654+
"t.s\n"
655+
" a: object\n"
656+
" Param docstring with trailing whitespace \n"
657+
"Func docstring summary with trailing whitespace \n"
658+
" \n"
659+
"Func docstring body with trailing whitespace \n"
660+
)
661+
self.checkDocstring(function, """
662+
s($module, /, a)
663+
--
664+
665+
Func docstring summary with trailing whitespace
666+
667+
a
668+
Param docstring with trailing whitespace
669+
670+
Func docstring body with trailing whitespace
671+
""")
672+
651673
def test_explicit_parameters_in_docstring(self):
652674
function = self.parse_function(dedent("""
653675
module foo

Tools/clinic/clinic.py

+23-32
Original file line numberDiff line numberDiff line change
@@ -4617,15 +4617,21 @@ def parse(self, block: Block) -> None:
46174617
fail("'preserve' only works for blocks that don't produce any output!")
46184618
block.output = self.saved_output
46194619

4620-
@staticmethod
4621-
def valid_line(line: str) -> bool:
4620+
def in_docstring(self) -> bool:
4621+
"""Return true if we are processing a docstring."""
4622+
return self.state in {
4623+
self.state_parameter_docstring,
4624+
self.state_function_docstring,
4625+
}
4626+
4627+
def valid_line(self, line: str) -> bool:
46224628
# ignore comment-only lines
46234629
if line.lstrip().startswith('#'):
46244630
return False
46254631

46264632
# Ignore empty lines too
46274633
# (but not in docstring sections!)
4628-
if not line.strip():
4634+
if not self.in_docstring() and not line.strip():
46294635
return False
46304636

46314637
return True
@@ -5262,12 +5268,20 @@ def state_parameter_docstring_start(self, line: str) -> None:
52625268
assert self.indent.depth == 3
52635269
return self.next(self.state_parameter_docstring, line)
52645270

5271+
def docstring_append(self, obj: Function | Parameter, line: str) -> None:
5272+
"""Add a rstripped line to the current docstring."""
5273+
docstring = obj.docstring
5274+
if docstring:
5275+
docstring += "\n"
5276+
if stripped := line.rstrip():
5277+
docstring += self.indent.dedent(stripped)
5278+
obj.docstring = docstring
5279+
52655280
# every line of the docstring must start with at least F spaces,
52665281
# where F > P.
52675282
# these F spaces will be stripped.
52685283
def state_parameter_docstring(self, line: str) -> None:
5269-
stripped = line.strip()
5270-
if stripped.startswith('#'):
5284+
if not self.valid_line(line):
52715285
return
52725286

52735287
indent = self.indent.measure(line)
@@ -5281,16 +5295,8 @@ def state_parameter_docstring(self, line: str) -> None:
52815295
return self.next(self.state_function_docstring, line)
52825296

52835297
assert self.function and self.function.parameters
5284-
last_parameter = next(reversed(list(self.function.parameters.values())))
5285-
5286-
new_docstring = last_parameter.docstring
5287-
5288-
if new_docstring:
5289-
new_docstring += '\n'
5290-
if stripped:
5291-
new_docstring += self.indent.dedent(line)
5292-
5293-
last_parameter.docstring = new_docstring
5298+
last_param = next(reversed(self.function.parameters.values()))
5299+
self.docstring_append(last_param, line)
52945300

52955301
# the final stanza of the DSL is the docstring.
52965302
def state_function_docstring(self, line: str) -> None:
@@ -5299,19 +5305,10 @@ def state_function_docstring(self, line: str) -> None:
52995305
if self.group:
53005306
fail("Function " + self.function.name + " has a ] without a matching [.")
53015307

5302-
stripped = line.strip()
5303-
if stripped.startswith('#'):
5308+
if not self.valid_line(line):
53045309
return
53055310

5306-
new_docstring = self.function.docstring
5307-
if new_docstring:
5308-
new_docstring += "\n"
5309-
if stripped:
5310-
line = self.indent.dedent(line).rstrip()
5311-
else:
5312-
line = ''
5313-
new_docstring += line
5314-
self.function.docstring = new_docstring
5311+
self.docstring_append(self.function, line)
53155312

53165313
def format_docstring(self) -> str:
53175314
f = self.function
@@ -5580,12 +5577,6 @@ def do_post_block_processing_cleanup(self) -> None:
55805577
if no_parameter_after_star:
55815578
fail("Function " + self.function.name + " specifies '*' without any parameters afterwards.")
55825579

5583-
# remove trailing whitespace from all parameter docstrings
5584-
for name, value in self.function.parameters.items():
5585-
if not value:
5586-
continue
5587-
value.docstring = value.docstring.rstrip()
5588-
55895580
self.function.docstring = self.format_docstring()
55905581

55915582

0 commit comments

Comments
 (0)