Repository: PyCQA/docformatter (python)

Issue:
No wrapping when there is a paragraph break
example:
```python
def func():
    """My awesome function.

    This line is quite long. In fact is it longer than one hundred and twenty characters so it should be wrapped but it is not.

    It doesn't wrap because of this line and the blank line in between! Delete them and it will wrap.
    """
```

When running docformatter it doesn't wrap, eg:
```
docformatter example.py --wrap-descriptions 120 --wrap-summaries 120
```

docformatter 1.5.0

Pull request:
fix: wrap multi-paragraph long descriptions
This pull request fixes the problem with docformatter not wrapping long descriptions that have multiple paragraphs.

Closes #127

Patch:
diff --git a/.github/workflows/do-release.yml b/.github/workflows/do-release.yml
index b8560b2..1ef0225 100644
--- a/.github/workflows/do-release.yml
+++ b/.github/workflows/do-release.yml
@@ -99,7 +99,7 @@ jobs:
 
       - name: Upload wheel to Release
         id: upload-wheel
-#        if: ${{ env.install_ok == 1 }}
+        if: ${{ env.build_ok == 1 }}
         uses: shogo82148/actions-upload-release-asset@v1
         with:
           upload_url: ${{ steps.cutrelease.outputs.upload_url }}
diff --git a/src/docformatter/syntax.py b/src/docformatter/syntax.py
index 127ea2b..119b156 100644
--- a/src/docformatter/syntax.py
+++ b/src/docformatter/syntax.py
@@ -32,6 +32,52 @@ from typing import List
 HEURISTIC_MIN_LIST_ASPECT_RATIO = 0.4
 
 
+def description_to_list(
+    text: str, indentation: str, wrap_length: int
+) -> List[str]:
+    """Convert the description to a list of wrap length lines.
+
+    Parameters
+    ----------
+    text : str
+        The docstring description.
+    indentation : str
+        The indentation (number of spaces or tabs) to place in front of each
+        line.
+    wrap_length : int
+        The column to wrap each line at.
+
+    Returns
+    -------
+    lines : list
+        A list containing each line of the description with any links put
+        back together.
+    """
+    # This is a description containing only one paragraph.
+    if len(re.findall(r"\n\n", text)) <= 0:
+        return textwrap.wrap(
+            textwrap.dedent(text),
+            width=wrap_length,
+            initial_indent=indentation,
+            subsequent_indent=indentation,
+        )
+
+    # This is a description containing multiple paragraphs.
+    lines = []
+    for _line in text.splitlines():
+        _text = textwrap.wrap(
+            textwrap.dedent(_line),
+            width=wrap_length,
+            initial_indent=indentation,
+            subsequent_indent=indentation,
+        )
+        if _text:
+            lines.extend(_text)
+        else:
+            lines.append("")
+    return lines
+
+
 def do_preserve_links(
     text: str,
     indentation: str,
@@ -55,12 +101,7 @@ def do_preserve_links(
         A list containing each line of the description with any links put
         back together.
     """
-    lines = textwrap.wrap(
-        textwrap.dedent(text),
-        width=wrap_length,
-        initial_indent=indentation,
-        subsequent_indent=indentation,
-    )
+    lines = description_to_list(text, indentation, wrap_length)
 
     # There is nothing to do if the input wasn't wrapped.
     if len(lines) < 2:
@@ -157,8 +198,8 @@ def is_some_sort_of_list(text, strict) -> bool:
 
     return any(
         (
-            re.match(r"\s*$", line)
-            or
+            # re.match(r"\s*$", line)
+            # or
             # "1. item"
             re.match(r"\s*\d\.", line)
             or


Test patch:
diff --git a/tests/test_docformatter.py b/tests/test_docformatter.py
index 7a6fd96..f4dad41 100644
--- a/tests/test_docformatter.py
+++ b/tests/test_docformatter.py
@@ -736,12 +736,12 @@ pre-summary-space = false
         "contents",
         [
             '''\
-            class TestFoo():
-                """Docstring that should have a pre-summary newline.
-                
-                This is a multi-line docstring that should have a newline 
-                placed before the summary."""
-            '''
+class TestFoo():
+    """Docstring that should have a pre-summary newline.
+
+    This is a multi-line docstring that should have a newline 
+    placed before the summary."""
+'''
         ],
     )
     @pytest.mark.parametrize(
@@ -776,19 +776,17 @@ pre-summary-space = false
         See issue #119.
         """
         assert '''\
-@@ -1,6 +1,7 @@
-             class TestFoo():
--                """Docstring that should have a pre-summary newline.
--                
--                This is a multi-line docstring that should have a newline 
--                placed before the summary."""
--            
-+                """
-+                Docstring that should have a pre-summary newline.
-+
-+                This is a multi-line docstring that should have a
-+                newline placed before the summary.
-+                """
+@@ -1,5 +1,7 @@
+ class TestFoo():
+-    """Docstring that should have a pre-summary newline.
++    """
++    Docstring that should have a pre-summary newline.
+ 
+-    This is a multi-line docstring that should have a newline 
+-    placed before the summary."""
++    This is a multi-line docstring that should have a newline placed
++    before the summary.
++    """
 ''' == "\n".join(
             run_docformatter.communicate()[0]
             .decode()
@@ -915,13 +913,13 @@ pre-summary-space = false
         "contents",
         [
             '''\
-            class TestFoo():
-                """Summary docstring that is followed by a description.
-                
-                This is the description and it shouldn't have a blank line 
-                inserted after it.
-                """
-            '''
+class TestFoo():
+    """Summary docstring that is followed by a description.
+
+    This is the description and it shouldn't have a blank line 
+    inserted after it.
+    """
+'''
         ],
     )
     @pytest.mark.parametrize(
@@ -956,17 +954,15 @@ pre-summary-space = false
         See issue #119.
         """
         assert '''\
-@@ -1,7 +1,6 @@
-             class TestFoo():
-                 """Summary docstring that is followed by a description.
--                
--                This is the description and it shouldn\'t have a blank line 
--                inserted after it.
-+
-+                This is the description and it shouldn\'t have a blank
-+                line inserted after it.
-                 """
--            
+@@ -1,6 +1,6 @@
+ class TestFoo():
+     """Summary docstring that is followed by a description.
+ 
+-    This is the description and it shouldn\'t have a blank line 
+-    inserted after it.
++    This is the description and it shouldn\'t have a blank line inserted
++    after it.
+     """
 ''' == "\n".join(
             run_docformatter.communicate()[0]
             .decode()
@@ -979,22 +975,22 @@ pre-summary-space = false
         "contents",
         [
             '''\
-                class TestFoo():
-                    """Summary docstring that is followed by a description.
+class TestFoo():
+    """Summary docstring that is followed by a description.
 
-                    This is the description and it should have a blank line 
-                    inserted after it.
-                    """
-                '''
+    This is the description and it should have a blank line 
+    inserted after it.
+    """
+'''
         ],
     )
     @pytest.mark.parametrize(
         "config",
         [
             """\
-                            [tool.docformatter]
-                            blank = true
-                            """
+            [tool.docformatter]
+            blank = true
+            """
         ],
     )
     @pytest.mark.parametrize(
@@ -1020,17 +1016,16 @@ pre-summary-space = false
         See issue #119.
         """
         assert '''\
-@@ -1,7 +1,7 @@
-                 class TestFoo():
-                     """Summary docstring that is followed by a description.
+@@ -1,6 +1,7 @@
+ class TestFoo():
+     """Summary docstring that is followed by a description.
  
--                    This is the description and it should have a blank line 
--                    inserted after it.
-+                    This is the description and it should have a blank
-+                    line inserted after it.
+-    This is the description and it should have a blank line 
+-    inserted after it.
++    This is the description and it should have a blank line inserted
++    after it.
 +
-                     """
--                
+     """
 ''' == "\n".join(
             run_docformatter.communicate()[0]
             .decode()
diff --git a/tests/test_format_docstring.py b/tests/test_format_docstring.py
index 99c34e4..8235e96 100644
--- a/tests/test_format_docstring.py
+++ b/tests/test_format_docstring.py
@@ -437,6 +437,43 @@ class TestFormatWrap:
                 if len(line.split()) > 1:
                     assert len(line) <= max_length
 
+    @pytest.mark.unit
+    @pytest.mark.parametrize(
+        "args", [["--wrap-descriptions", "120", "--wrap-summaries", "120", ""]]
+    )
+    def test_format_docstring_with_multi_paragraph_description(
+        self,
+        test_args,
+        args,
+    ):
+        """Wrap each paragraph in the long description seperately.
+
+        See issue #127.
+        """
+        uut = Formatter(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '''\
+"""My awesome function.
+
+    This line is quite long. In fact is it longer than one hundred and twenty characters so it should be wrapped but it
+    is not.
+
+    It doesn\'t wrap because of this line and the blank line in between! Delete them and it will wrap.
+    """''' == uut._do_format_docstring(
+            INDENTATION,
+            '''"""My awesome function.
+
+    This line is quite long. In fact is it longer than one hundred and twenty characters so it should be wrapped but it is not.
+
+    It doesn't wrap because of this line and the blank line in between! Delete them and it will wrap.
+    """'''.strip(),
+        )
+
     @pytest.mark.unit
     @pytest.mark.parametrize("args", [["--wrap-summaries", "79", ""]])
     def test_format_docstring_with_weird_indentation_and_punctuation(
@@ -803,9 +840,9 @@ num_iterations is the number of updates - instead of a better definition of conv
         [["--wrap-descriptions", "72", ""]],
     )
     def test_format_docstring_with_short_inline_link(
-            self,
-            test_args,
-            args,
+        self,
+        test_args,
+        args,
     ):
         """Short in-line links will remain untouched.
 
@@ -836,23 +873,23 @@ num_iterations is the number of updates - instead of a better definition of conv
 
     @pytest.mark.unit
     @pytest.mark.parametrize(
-            "args",
-            [["--wrap-descriptions", "72", ""]],
-        )
+        "args",
+        [["--wrap-descriptions", "72", ""]],
+    )
     def test_format_docstring_with_inline_link_retain_spaces(
-                self,
-                test_args,
-                args,
+        self,
+        test_args,
+        args,
     ):
         """In-line links shouldn't remove blank spaces between words.
 
         See issue #140.
         """
         uut = Formatter(
-                test_args,
-                sys.stderr,
-                sys.stdin,
-                sys.stdout,
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
         )
 
         docstring = '''\
@@ -870,8 +907,8 @@ num_iterations is the number of updates - instead of a better definition of conv
     details.
     """\
 ''' == uut._do_format_docstring(
-                INDENTATION, docstring.strip()
-            )
+            INDENTATION, docstring.strip()
+        )
 
     @pytest.mark.unit
     @pytest.mark.parametrize(