From abe0f3f3dc444fd12faf017e8ad73c941608e120 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Fri, 7 Jul 2023 13:02:47 -0400 Subject: [PATCH] docs: fix formatting of docstring with lists (#1687) * add test to show issue with lists * apply fix * work around click issue with mypy * style * apply more fixes for lists * formatting * add comment * style * coverage * move tests to test_lines * coverage * style * add test --- gapic/utils/lines.py | 16 ++++++-- noxfile.py | 10 ++++- tests/unit/utils/test_lines.py | 68 ++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/gapic/utils/lines.py b/gapic/utils/lines.py index 9e66a88ae9..fffa125e80 100644 --- a/gapic/utils/lines.py +++ b/gapic/utils/lines.py @@ -86,8 +86,12 @@ def wrap(text: str, width: int, *, offset: Optional[int] = None, indent: int = 0 break_on_hyphens=False, ) # Strip the first \n from the text so it is not misidentified as an - # intentionally short line below. - text = text.replace('\n', ' ', 1) + # intentionally short line below, except when the text contains `:` + # as the new line is required for lists. + if '\n' in text: + initial_text = text.split('\n')[0] + if ":" not in initial_text: + text = text.replace('\n', ' ', 1) # Save the new `first` line. first = f'{initial[0]}\n' @@ -100,6 +104,10 @@ def wrap(text: str, width: int, *, offset: Optional[int] = None, indent: int = 0 tokens = [] token = '' for line in text.split('\n'): + # Ensure that lines that start with a hyphen are always on a new line + if line.strip().startswith('-') and token: + tokens.append(token) + token = '' token += line + '\n' if len(line) < width * 0.75: tokens.append(token) @@ -115,7 +123,9 @@ def wrap(text: str, width: int, *, offset: Optional[int] = None, indent: int = 0 text='\n'.join([textwrap.fill( break_long_words=False, initial_indent=' ' * indent, - subsequent_indent=' ' * indent, + # ensure that subsequent lines for lists are indented 2 spaces + subsequent_indent=' ' * indent + \ + (' ' if token.strip().startswith('-') else ''), text=token, width=width, break_on_hyphens=False, diff --git a/noxfile.py b/noxfile.py index afe77cfd9a..3786be6c17 100644 --- a/noxfile.py +++ b/noxfile.py @@ -436,7 +436,13 @@ def docs(session): @nox.session(python=NEWEST_PYTHON) def mypy(session): """Perform typecheck analysis.""" - - session.install("mypy", "types-protobuf<=3.19.7", "types-PyYAML", "types-dataclasses") + # Pin to click==8.1.3 to workaround https://github.com/pallets/click/issues/2558 + session.install( + "mypy", + "types-protobuf<=3.19.7", + "types-PyYAML", + "types-dataclasses", + "click==8.1.3", + ) session.install(".") session.run("mypy", "gapic") diff --git a/tests/unit/utils/test_lines.py b/tests/unit/utils/test_lines.py index c471cb677b..51dccf5c39 100644 --- a/tests/unit/utils/test_lines.py +++ b/tests/unit/utils/test_lines.py @@ -91,3 +91,71 @@ def test_wrap_short_line_preserved(): def test_wrap_does_not_break_hyphenated_word(): assert lines.wrap('do-not-break', width=5) == 'do-not-break' + + +def test_wrap_with_short_lines(): + input = """The hail in Wales falls mainly on the snails. The hail in Wales falls mainly +on the snails.""" + expected = """The hail in Wales falls mainly on the snails. The hail in +Wales falls mainly on the snails.""" + assert lines.wrap(input, width=60) == expected + + +def test_list_each_item_in_list_has_new_line(): + s = """Type of weather: +- Hail +- Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain +- Snow""" + assert lines.wrap(s, width=80) == s + + +def test_list_items_are_indented(): + input = """Type of weather. +Some types of weather: + +- A mix of hail and snow, followed by rain clouds, then finally clear sky +- Rain +- Snow""" + expected = """Type of weather. +Some types of weather: + +- A mix of hail and snow, followed by rain clouds, then + finally clear sky +- Rain +- Snow""" + assert lines.wrap(input, width=60) == expected + + +def test_list_new_line_preserved_after_colon(): + input = """Today's forecast will have different types of weather: + +- A mix of hail and snow, followed by rain clouds, then finally clear sky +- Rain +- Snow""" + expected = """Today's forecast will have different types + of weather: + + - A mix of hail and snow, followed by rain + clouds, then finally clear sky + - Rain + - Snow""" + assert lines.wrap(input, width=60, indent=16) == expected + + +def test_list_items_longer_text_before_list(): + input = """Weather Weather Weather Weather Weather Weather Weather +Weather Weather Weather Weather Weather Weather Weather +Type of weather: + +- Hail +- Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain +- Snow""" + expected = """Weather Weather Weather Weather Weather Weather Weather +Weather Weather Weather Weather Weather Weather Weather Type +of weather: + +- Hail +- Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain Rain + Rain +- Snow""" + assert lines.wrap(input, width=60) == expected