From 02f859f34ae4c61a2946dc4a768d7515540158f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Tue, 19 Apr 2022 16:58:43 +0200 Subject: [PATCH 1/2] Pass more arguments to ``_treat_summary`` --- pydocstringformatter/formatting/base.py | 20 +++++++++++++++++--- pydocstringformatter/formatting/formatter.py | 16 ++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/pydocstringformatter/formatting/base.py b/pydocstringformatter/formatting/base.py index e7ca39d6..8f5c220e 100644 --- a/pydocstringformatter/formatting/base.py +++ b/pydocstringformatter/formatting/base.py @@ -101,7 +101,13 @@ class SummaryAndDescriptionFormatter(StringAndQuotesFormatter): """Base class for formatter that modifies the summary and description.""" @abc.abstractmethod - def _treat_summary(self, summary: str, indent_length: int) -> str: + def _treat_summary( + self, + summary: str, + indent_length: int, + quotes_length: Literal[1, 3], + description_exists: bool, + ) -> str: """Return a modified summary.""" @abc.abstractmethod @@ -156,7 +162,9 @@ def _treat_string( quotes_length, ) - new_summary = self._treat_summary(summary, indent_length) + new_summary = self._treat_summary( + summary, indent_length, quotes_length, bool(description) + ) docstring = f"{quotes}{prefix}{new_summary}" if description: @@ -173,7 +181,13 @@ class SummaryFormatter(SummaryAndDescriptionFormatter): """Base class for formatter that only modifies the summary of a docstring.""" @abc.abstractmethod - def _treat_summary(self, summary: str, indent_length: int) -> str: + def _treat_summary( + self, + summary: str, + indent_length: int, + quotes_length: Literal[1, 3], + description_exists: bool, + ) -> str: """Return a modified summary.""" def _treat_description(self, description: str, indent_length: int) -> str: diff --git a/pydocstringformatter/formatting/formatter.py b/pydocstringformatter/formatting/formatter.py index 9a00e63d..956a5719 100644 --- a/pydocstringformatter/formatting/formatter.py +++ b/pydocstringformatter/formatting/formatter.py @@ -83,7 +83,13 @@ class FinalPeriodFormatter(SummaryFormatter): name = "final-period" END_OF_SENTENCE_PUNCTUATION = {".", "?", "!", "‽", ":", ";"} - def _treat_summary(self, summary: str, indent_length: int) -> str: + def _treat_summary( + self, + summary: str, + indent_length: int, + quotes_length: Literal[1, 3], + description_exists: bool, + ) -> str: """Add a period to the end of single-line docstrings and summaries.""" if summary[-1] in self.END_OF_SENTENCE_PUNCTUATION: return summary @@ -120,7 +126,13 @@ class SplitSummaryAndDocstringFormatter(SummaryFormatter): """Pattern to match against an end of sentence period.""" # pylint: disable-next=too-many-branches - def _treat_summary(self, summary: str, indent_length: int) -> str: + def _treat_summary( + self, + summary: str, + indent_length: int, + quotes_length: Literal[1, 3], + description_exists: bool, + ) -> str: """Split a summary and body if there is a period after the summary.""" new_summary = None From 5ac722b8b84e1eaa02f9fa079a53e1e95e1086c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Tue, 19 Apr 2022 17:05:39 +0200 Subject: [PATCH 2/2] Add ``LineWrapperFormatter`` --- docs/usage.rst | 5 +++ pydocstringformatter/formatting/__init__.py | 2 + pydocstringformatter/formatting/formatter.py | 45 +++++++++++++++++++ .../linewrap_summary/function_docstrings.args | 1 + .../linewrap_summary/function_docstrings.py | 27 +++++++++++ .../function_docstrings.py.out | 31 +++++++++++++ 6 files changed, 111 insertions(+) create mode 100644 tests/data/format/linewrap_summary/function_docstrings.args create mode 100644 tests/data/format/linewrap_summary/function_docstrings.py create mode 100644 tests/data/format/linewrap_summary/function_docstrings.py.out diff --git a/docs/usage.rst b/docs/usage.rst index 64b252eb..1ce127f0 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -11,6 +11,7 @@ Current usage of ``pydocstringformatter``: [--summary-quotes-same-line] [--split-summary-body --no-split-summary-body] [--strip-whitespaces --no-strip-whitespaces] + [--linewrap-full-docstring --no-linewrap-full-docstring] [--beginning-quotes --no-beginning-quotes] [--closing-quotes --no-closing-quotes] [--capitalize-first-letter --no-capitalize-first-letter] @@ -79,3 +80,7 @@ Current usage of ``pydocstringformatter``: currently optional as its considered somwehat opinionated and might require major refactoring for existing projects. (default: False) + --linewrap-full-docstring, --no-linewrap-full-docstring + Activate or deactivate linewrap-full-docstring: + Linewrap the docstring by the pre-defined line length. + (default: False) diff --git a/pydocstringformatter/formatting/__init__.py b/pydocstringformatter/formatting/__init__.py index f57b7fa4..ebe74578 100644 --- a/pydocstringformatter/formatting/__init__.py +++ b/pydocstringformatter/formatting/__init__.py @@ -8,6 +8,7 @@ CapitalizeFirstLetterFormatter, ClosingQuotesFormatter, FinalPeriodFormatter, + LineWrapperFormatter, QuotesTypeFormatter, SplitSummaryAndDocstringFormatter, StripWhitespacesFormatter, @@ -21,6 +22,7 @@ FORMATTERS: List[Formatter] = [ SplitSummaryAndDocstringFormatter(), StripWhitespacesFormatter(), + LineWrapperFormatter(), BeginningQuotesFormatter(), ClosingQuotesFormatter(), CapitalizeFirstLetterFormatter(), diff --git a/pydocstringformatter/formatting/formatter.py b/pydocstringformatter/formatting/formatter.py index 956a5719..8a6d8f97 100644 --- a/pydocstringformatter/formatting/formatter.py +++ b/pydocstringformatter/formatting/formatter.py @@ -1,4 +1,5 @@ import re +import textwrap import tokenize from typing import Literal @@ -54,6 +55,50 @@ def _treat_string(self, tokeninfo: tokenize.TokenInfo, _: int) -> str: return new_string or tokeninfo.string +class LineWrapperFormatter(SummaryFormatter): + """Linewrap the docstring by the pre-defined line length.""" + + name = "linewrap-full-docstring" + optional = True + + def _treat_summary( + self, + summary: str, + indent_length: int, + quotes_length: Literal[1, 3], + description_exists: bool, + ) -> str: + """Wrap the summary of a docstring.""" + + # We need to deduct ending quotes if there is no description + line_length = 88 if description_exists else 88 - quotes_length + summary_lines = summary.splitlines() + + new_summary = "\n".join( + textwrap.wrap( + summary_lines[0], + width=line_length, + initial_indent=" " * (indent_length + quotes_length), + subsequent_indent=" " * indent_length, + replace_whitespace=True, + ) + )[indent_length + quotes_length :] + + if len(summary_lines) > 1: + for line in summary_lines[1:]: + new_summary += "\n" + new_summary += "\n".join( + textwrap.wrap( + line, + width=line_length, + subsequent_indent=" " * indent_length, + replace_whitespace=True, + ) + ) + + return new_summary + + class ClosingQuotesFormatter(StringFormatter): """Fix the position of the closing quotes.""" diff --git a/tests/data/format/linewrap_summary/function_docstrings.args b/tests/data/format/linewrap_summary/function_docstrings.args new file mode 100644 index 00000000..867f4488 --- /dev/null +++ b/tests/data/format/linewrap_summary/function_docstrings.args @@ -0,0 +1 @@ +--linewrap-full-docstring diff --git a/tests/data/format/linewrap_summary/function_docstrings.py b/tests/data/format/linewrap_summary/function_docstrings.py new file mode 100644 index 00000000..3f1d8318 --- /dev/null +++ b/tests/data/format/linewrap_summary/function_docstrings.py @@ -0,0 +1,27 @@ +def func(): + """A very long summary line that needs to be wrapped. A very long summary line that needs to be wrapped. + + A description that is not too long. + """ + + +def func(): + """A very long multi-line summary line that needs to be wrapped. A very long multi-line summary line that needs to be wrapped. + A very long summary line that needs to be wrapped. + + A description that is not too long. + """ + + +def func(): + """A multi-line summary that can be on one line. + But it has a new line so it isn't. + + A description that is not too long. + """ + + +# Since the ending quotes will be appended on the same line this +# exceeds the max length. +def func(): + """A multi-line summary that can be on one line. Something that is just too longgg.""" diff --git a/tests/data/format/linewrap_summary/function_docstrings.py.out b/tests/data/format/linewrap_summary/function_docstrings.py.out new file mode 100644 index 00000000..847df4af --- /dev/null +++ b/tests/data/format/linewrap_summary/function_docstrings.py.out @@ -0,0 +1,31 @@ +def func(): + """A very long summary line that needs to be wrapped. A very long summary line that + needs to be wrapped. + + A description that is not too long. + """ + + +def func(): + """A very long multi-line summary line that needs to be wrapped. A very long multi- + line summary line that needs to be wrapped. + A very long summary line that needs to be wrapped. + + A description that is not too long. + """ + + +def func(): + """A multi-line summary that can be on one line. + But it has a new line so it isn't. + + A description that is not too long. + """ + + +# Since the ending quotes will be appended on the same line this +# exceeds the max length. +def func(): + """A multi-line summary that can be on one line. Something that is just too + longgg. + """