Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: wrap multi-paragraph long descriptions #143

Merged
merged 3 commits into from
Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/do-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
57 changes: 49 additions & 8 deletions src/docformatter/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,52 @@
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,
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down
105 changes: 50 additions & 55 deletions tests/test_docformatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,12 +736,12 @@ class TestFoo():
"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(
Expand Down Expand Up @@ -776,19 +776,17 @@ def test_pre_summary_newline_using_pyproject(
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()
Expand Down Expand Up @@ -915,13 +913,13 @@ class TestFoo():
"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(
Expand Down Expand Up @@ -956,17 +954,15 @@ def test_no_blank_using_pyproject(
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()
Expand All @@ -979,22 +975,22 @@ class TestFoo():
"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(
Expand All @@ -1020,17 +1016,16 @@ def test_blank_using_pyproject(
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()
Expand Down
67 changes: 52 additions & 15 deletions tests/test_format_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,43 @@ def test_format_docstring_with_wrap(
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(
Expand Down Expand Up @@ -803,9 +840,9 @@ def test_format_docstring_with_inline_links(
[["--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.

Expand Down Expand Up @@ -836,23 +873,23 @@ def test_format_docstring_with_short_inline_link(

@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 = '''\
Expand All @@ -870,8 +907,8 @@ def test_format_docstring_with_inline_link_retain_spaces(
details.
"""\
''' == uut._do_format_docstring(
INDENTATION, docstring.strip()
)
INDENTATION, docstring.strip()
)

@pytest.mark.unit
@pytest.mark.parametrize(
Expand Down