From 96767a5507e4aa31fa9ab882e21142e8437c24cc Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 4 Aug 2023 15:40:15 +0200 Subject: [PATCH 1/2] PoC: Move docstring assert to the DSL state machine --- Lib/test/test_clinic.py | 2 +- Tools/clinic/clinic.py | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index dd17d02519bfa7..7f7420c2855ccd 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1921,7 +1921,7 @@ def test_state_func_docstring_no_summary(self): docstring1 docstring2 """ - self.expect_failure(block, err, lineno=0) + self.expect_failure(block, err, lineno=3) def test_state_func_docstring_only_one_param_template(self): err = "You may not specify {parameters} more than once in a docstring!" diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 6c5a2c7c857c53..65659cae778c3e 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -4631,7 +4631,8 @@ def in_docstring(self) -> bool: """Return true if we are processing a docstring.""" return self.state in { self.state_parameter_docstring, - self.state_function_docstring, + self.state_function_docstring_separator, + self.state_function_docstring_body, } def valid_line(self, line: str) -> bool: @@ -4732,7 +4733,7 @@ def state_modulename_name(self, line: str) -> None: self.function = function self.block.signatures.append(function) (cls or module).functions.append(function) - self.next(self.state_function_docstring) + self.next(self.state_function_docstring_start) return line, _, returns = line.partition('->') @@ -4866,7 +4867,7 @@ def state_parameters_start(self, line: str) -> None: # if this line is not indented, we have no parameters if not self.indent.infer(line): - return self.next(self.state_function_docstring, line) + return self.next(self.state_function_docstring_start, line) self.parameter_continuation = '' return self.next(self.state_parameter, line) @@ -4896,7 +4897,7 @@ def state_parameter(self, line: str) -> None: indent = self.indent.infer(line) if indent == -1: # we outdented, must be to definition column - return self.next(self.state_function_docstring, line) + return self.next(self.state_function_docstring_start, line) if indent == 1: # we indented, must be to new parameter docstring column @@ -5317,22 +5318,41 @@ def state_parameter_docstring(self, line: str) -> None: # back to a parameter return self.next(self.state_parameter, line) assert self.indent.depth == 1 - return self.next(self.state_function_docstring, line) + return self.next(self.state_function_docstring_start, line) assert self.function and self.function.parameters last_param = next(reversed(self.function.parameters.values())) self.docstring_append(last_param, line) # the final stanza of the DSL is the docstring. - def state_function_docstring(self, line: str) -> None: + def state_function_docstring_start(self, line: str) -> None: assert self.function is not None - if self.group: fail(f"Function {self.function.name!r} has a ']' without a matching '['.") + return self.next(self.state_function_docstring_summary, line) + def state_function_docstring_summary(self, line: str) -> None: + assert self.function is not None if not self.valid_line(line): return + self.docstring_append(self.function, line) + self.next(self.state_function_docstring_separator) + def state_function_docstring_separator(self, line: str) -> None: + assert self.function is not None + if not self.valid_line(line): + return + if line.strip() != "": + fail(f"Docstring for {self.function.full_name!r} does not have a summary line!\n" + "Every non-blank function docstring must start with " + "a single line summary followed by an empty line.") + self.docstring_append(self.function, line) + self.next(self.state_function_docstring_body) + + def state_function_docstring_body(self, line: str) -> None: + assert self.function is not None + if not self.valid_line(line): + return self.docstring_append(self.function, line) def format_docstring(self) -> str: @@ -5553,12 +5573,7 @@ def add_parameter(text: str) -> None: # Guido said Clinic should enforce this: # http://mail.python.org/pipermail/python-dev/2013-June/127110.html - if len(lines) >= 2: - if lines[1]: - fail(f"Docstring for {f.full_name!r} does not have a summary line!\n" - "Every non-blank function docstring must start with " - "a single line summary followed by an empty line.") - elif len(lines) == 1: + if len(lines) == 1: # the docstring is only one line right now--the summary line. # add an empty line after the summary line so we have space # between it and the {parameters} we're about to add. From 55b8d50a204ecaae585ce9ab97f4977cc6318d8d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 4 Aug 2023 16:08:13 +0200 Subject: [PATCH 2/2] Also move comment --- Tools/clinic/clinic.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 65659cae778c3e..f97e5bb4c7a4c4 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5342,7 +5342,19 @@ def state_function_docstring_separator(self, line: str) -> None: assert self.function is not None if not self.valid_line(line): return + if line.strip() != "": + # Enforce the summary line! + # The first line of a docstring should be a summary of the function. + # It should fit on one line (80 columns? 79 maybe?) and be a paragraph + # by itself. + # + # Argument Clinic enforces the following rule: + # * either the docstring is empty, + # * or it must have a summary line. + # + # Guido said Clinic should enforce this: + # http://mail.python.org/pipermail/python-dev/2013-June/127110.html fail(f"Docstring for {self.function.full_name!r} does not have a summary line!\n" "Every non-blank function docstring must start with " "a single line summary followed by an empty line.") @@ -5560,19 +5572,6 @@ def add_parameter(text: str) -> None: docstring = f.docstring.rstrip() lines = [line.rstrip() for line in docstring.split('\n')] - - # Enforce the summary line! - # The first line of a docstring should be a summary of the function. - # It should fit on one line (80 columns? 79 maybe?) and be a paragraph - # by itself. - # - # Argument Clinic enforces the following rule: - # * either the docstring is empty, - # * or it must have a summary line. - # - # Guido said Clinic should enforce this: - # http://mail.python.org/pipermail/python-dev/2013-June/127110.html - if len(lines) == 1: # the docstring is only one line right now--the summary line. # add an empty line after the summary line so we have space