From 1fc3215e8c8856094b20d497e4e0e3e547ed38eb Mon Sep 17 00:00:00 2001 From: Mark Bell Date: Thu, 22 Apr 2021 16:23:41 +0100 Subject: [PATCH] Make black remove leading and trailing spaces from one-line docstrings (#1740) Fixes #1738. Fixes #1812. Previously, Black removed leading and trailing spaces in multiline docstrings but failed to remove them from one-line docstrings. --- CHANGES.md | 3 +++ src/black/__init__.py | 40 ++++++++++++++++++++++++------------ src/black_primer/primer.json | 10 ++++----- tests/data/docstring.py | 36 +++++++++++++++++++++++++++++++- tox.ini | 2 +- 5 files changed, 71 insertions(+), 20 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 12e8d45..f911fdf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,9 @@ #### _Black_ +- `Black` now processes one-line docstrings by stripping leading and trailing spaces, + and adding a padding space when needed to break up """". (#1740) + - `Black` now cleans up leading non-breaking spaces in comments (#2092) - `Black` now respects `--skip-string-normalization` when normalizing multiline diff --git a/src/black/__init__.py b/src/black/__init__.py index bb8f8c7..efa82f4 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -2153,16 +2153,35 @@ class LineGenerator(Visitor[Line]): # We're ignoring docstrings with backslash newline escapes because changing # indentation of those changes the AST representation of the code. prefix = get_string_prefix(leaf.value) - lead_len = len(prefix) + 3 - tail_len = -3 - indent = " " * 4 * self.current_line.depth - docstring = fix_docstring(leaf.value[lead_len:tail_len], indent) + docstring = leaf.value[len(prefix) :] # Remove the prefix + quote_char = docstring[0] + # A natural way to remove the outer quotes is to do: + # docstring = docstring.strip(quote_char) + # but that breaks on """""x""" (which is '""x'). + # So we actually need to remove the first character and the next two + # characters but only if they are the same as the first. + quote_len = 1 if docstring[1] != quote_char else 3 + docstring = docstring[quote_len:-quote_len] + + if is_multiline_string(leaf): + indent = " " * 4 * self.current_line.depth + docstring = fix_docstring(docstring, indent) + else: + docstring = docstring.strip() + if docstring: - if leaf.value[lead_len - 1] == docstring[0]: + # Add some padding if the docstring starts / ends with a quote mark. + if docstring[0] == quote_char: docstring = " " + docstring - if leaf.value[tail_len + 1] == docstring[-1]: + if docstring[-1] == quote_char: docstring = docstring + " " - leaf.value = leaf.value[0:lead_len] + docstring + leaf.value[tail_len:] + else: + # Add some padding if the docstring is empty. + docstring = " " + + # We could enforce triple quotes at this point. + quote = quote_char * quote_len + leaf.value = prefix + quote + docstring + quote yield from self.visit_default(leaf) @@ -6113,7 +6132,7 @@ def get_future_imports(node: Node) -> Set[str]: @lru_cache() def get_gitignore(root: Path) -> PathSpec: - """ Return a PathSpec matching gitignore content if present.""" + """Return a PathSpec matching gitignore content if present.""" gitignore = root / ".gitignore" lines: List[str] = [] if gitignore.is_file(): @@ -6953,11 +6972,6 @@ def patched_main() -> None: def is_docstring(leaf: Leaf) -> bool: - if not is_multiline_string(leaf): - # For the purposes of docstring re-indentation, we don't need to do anything - # with single-line docstrings. - return False - if prev_siblings_are( leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt] ): diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json index 32df015..3a1fae0 100644 --- a/src/black_primer/primer.json +++ b/src/black_primer/primer.json @@ -3,7 +3,7 @@ "projects": { "aioexabgp": { "cli_arguments": [], - "expect_formatting_changes": false, + "expect_formatting_changes": true, "git_clone_url": "https://github.com/cooperlees/aioexabgp.git", "long_checkout": false, "py_versions": ["all"] @@ -17,7 +17,7 @@ }, "bandersnatch": { "cli_arguments": [], - "expect_formatting_changes": false, + "expect_formatting_changes": true, "git_clone_url": "https://github.com/pypa/bandersnatch.git", "long_checkout": false, "py_versions": ["all"] @@ -84,7 +84,7 @@ }, "ptr": { "cli_arguments": [], - "expect_formatting_changes": false, + "expect_formatting_changes": true, "git_clone_url": "https://github.com/facebookincubator/ptr.git", "long_checkout": false, "py_versions": ["all"] @@ -98,14 +98,14 @@ }, "sqlalchemy": { "cli_arguments": [], - "expect_formatting_changes": false, + "expect_formatting_changes": true, "git_clone_url": "https://github.com/sqlalchemy/sqlalchemy.git", "long_checkout": false, "py_versions": ["all"] }, "tox": { "cli_arguments": [], - "expect_formatting_changes": false, + "expect_formatting_changes": true, "git_clone_url": "https://github.com/tox-dev/tox.git", "long_checkout": false, "py_versions": ["all"] diff --git a/tests/data/docstring.py b/tests/data/docstring.py index 5c6985d..74532b2 100644 --- a/tests/data/docstring.py +++ b/tests/data/docstring.py @@ -102,6 +102,23 @@ def and_this(): "hey yah"''' +def empty(): + ''' + + + + + ''' + + +def oneline_empty(): + ''' ''' + + +def single_quotes(): + 'testing' + + def believe_it_or_not_this_is_in_the_py_stdlib(): ''' "hey yah"''' @@ -110,6 +127,8 @@ def ignored_docstring(): """a => \ b""" +def single_line_docstring_with_whitespace(): + """ This should be stripped """ def docstring_with_inline_tabs_and_space_indentation(): """hey @@ -134,7 +153,6 @@ def docstring_with_inline_tabs_and_tab_indentation(): line ends with some tabs """ pass - # output @@ -241,6 +259,18 @@ def and_this(): "hey yah"''' +def empty(): + """ """ + + +def oneline_empty(): + """ """ + + +def single_quotes(): + "testing" + + def believe_it_or_not_this_is_in_the_py_stdlib(): ''' "hey yah"''' @@ -251,6 +281,10 @@ def ignored_docstring(): b""" +def single_line_docstring_with_whitespace(): + """This should be stripped""" + + def docstring_with_inline_tabs_and_space_indentation(): """hey diff --git a/tox.ini b/tox.ini index 9bb809a..71b02e9 100644 --- a/tox.ini +++ b/tox.ini @@ -23,4 +23,4 @@ commands = pip install -e .[d] coverage erase coverage run fuzz.py - coverage report \ No newline at end of file + coverage report -- 2.39.5