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

Make black remove leading and trailing spaces from one-line docstrings: fixes #1738 #1812 #1740

Merged
merged 17 commits into from Apr 22, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -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
Expand Down
40 changes: 27 additions & 13 deletions src/black/__init__.py
Expand Up @@ -2153,16 +2153,35 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[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)

Expand Down Expand Up @@ -6113,7 +6132,7 @@ def get_imports_from_children(children: List[LN]) -> Generator[str, None, None]:

@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():
Expand Down Expand Up @@ -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]
):
Expand Down
10 changes: 5 additions & 5 deletions src/black_primer/primer.json
Expand Up @@ -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"]
Expand 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"]
Expand Down Expand Up @@ -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"]
Expand 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"]
Expand Down
36 changes: 35 additions & 1 deletion tests/data/docstring.py
Expand Up @@ -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"'''

Expand All @@ -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
Expand All @@ -134,7 +153,6 @@ def docstring_with_inline_tabs_and_tab_indentation():
line ends with some tabs
"""
pass


# output

Expand Down Expand Up @@ -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"'''
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -23,4 +23,4 @@ commands =
pip install -e .[d]
coverage erase
coverage run fuzz.py
coverage report
coverage report