Skip to content

Commit

Permalink
Unify docstring detection (psf#4095)
Browse files Browse the repository at this point in the history
Co-authored-by: hauntsaninja <hauntsaninja@gmail.com>
  • Loading branch information
JelleZijlstra and hauntsaninja committed Dec 28, 2023
1 parent bf6cabc commit db9c592
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -14,6 +14,7 @@

<!-- Changes that affect Black's preview style -->

- Format module docstrings the same as class and function docstrings (#4095)
- Fix bug where spaces were not added around parenthesized walruses in subscripts,
unlike other binary operators (#4109)

Expand Down
4 changes: 2 additions & 2 deletions src/black/linegen.py
Expand Up @@ -424,7 +424,7 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
if Preview.hex_codes_in_unicode_sequences in self.mode:
normalize_unicode_escape_sequences(leaf)

if is_docstring(leaf) and not re.search(r"\\\s*\n", leaf.value):
if is_docstring(leaf, self.mode) and not re.search(r"\\\s*\n", leaf.value):
# We're ignoring docstrings with backslash newline escapes because changing
# indentation of those changes the AST representation of the code.
if self.mode.string_normalization:
Expand Down Expand Up @@ -477,7 +477,7 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
quote = quote_char * quote_len

# It's invalid to put closing single-character quotes on a new line.
if self.mode and quote_len == 3:
if quote_len == 3:
# We need to find the length of the last line of the docstring
# to find if we can add the closing quotes to the line without
# exceeding the maximum line length.
Expand Down
15 changes: 11 additions & 4 deletions src/black/lines.py
Expand Up @@ -196,7 +196,7 @@ def is_class_paren_empty(self) -> bool:
)

@property
def is_triple_quoted_string(self) -> bool:
def _is_triple_quoted_string(self) -> bool:
"""Is the line a triple quoted string?"""
if not self or self.leaves[0].type != token.STRING:
return False
Expand All @@ -209,6 +209,13 @@ def is_triple_quoted_string(self) -> bool:
return True
return False

@property
def is_docstring(self) -> bool:
"""Is the line a docstring?"""
if Preview.unify_docstring_detection not in self.mode:
return self._is_triple_quoted_string
return bool(self) and is_docstring(self.leaves[0], self.mode)

@property
def is_chained_assignment(self) -> bool:
"""Is the line a chained assignment"""
Expand Down Expand Up @@ -583,7 +590,7 @@ def maybe_empty_lines(self, current_line: Line) -> LinesBlock:
and self.previous_block
and self.previous_block.previous_block is None
and len(self.previous_block.original_line.leaves) == 1
and self.previous_block.original_line.is_triple_quoted_string
and self.previous_block.original_line.is_docstring
and not (current_line.is_class or current_line.is_def)
):
before = 1
Expand Down Expand Up @@ -690,7 +697,7 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
if (
self.previous_line
and self.previous_line.is_class
and current_line.is_triple_quoted_string
and current_line.is_docstring
):
if Preview.no_blank_line_before_class_docstring in current_line.mode:
return 0, 1
Expand All @@ -701,7 +708,7 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
is_empty_first_line_ok = (
Preview.allow_empty_first_line_in_block in current_line.mode
and (
not is_docstring(current_line.leaves[0])
not is_docstring(current_line.leaves[0], current_line.mode)
or (
self.previous_line
and self.previous_line.leaves[0]
Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Expand Up @@ -195,6 +195,7 @@ class Preview(Enum):
single_line_format_skip_with_multiple_comments = auto()
long_case_block_line_splitting = auto()
allow_form_feeds = auto()
unify_docstring_detection = auto()
respect_east_asian_width = auto()


Expand Down
12 changes: 11 additions & 1 deletion src/black/nodes.py
Expand Up @@ -531,14 +531,24 @@ def is_arith_like(node: LN) -> bool:
}


def is_docstring(leaf: Leaf) -> bool:
def is_docstring(leaf: Leaf, mode: Mode) -> bool:
if leaf.type != token.STRING:
return False

prefix = get_string_prefix(leaf.value)
if set(prefix).intersection("bBfF"):
return False

if (
Preview.unify_docstring_detection in mode
and leaf.parent
and leaf.parent.type == syms.simple_stmt
and not leaf.parent.prev_sibling
and leaf.parent.parent
and leaf.parent.parent.type == syms.file_input
):
return True

if prev_siblings_are(
leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt]
):
Expand Down
2 changes: 2 additions & 0 deletions src/black/strings.py
Expand Up @@ -63,6 +63,8 @@ def lines_with_leading_tabs_expanded(s: str) -> List[str]:
)
else:
lines.append(line)
if s.endswith("\n"):
lines.append("")
return lines


Expand Down
2 changes: 2 additions & 0 deletions tests/data/cases/module_docstring_2.py
@@ -1,6 +1,7 @@
# flags: --preview
"""I am a very helpful module docstring.
With trailing spaces:
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam,
Expand Down Expand Up @@ -38,6 +39,7 @@
# output
"""I am a very helpful module docstring.
With trailing spaces:
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam,
Expand Down
7 changes: 7 additions & 0 deletions tests/data/cases/preview_no_blank_line_before_docstring.py
Expand Up @@ -29,6 +29,9 @@ class MultilineDocstringsAsWell:
and on so many lines...
"""

class SingleQuotedDocstring:

"I'm a docstring but I don't even get triple quotes."

# output

Expand Down Expand Up @@ -57,3 +60,7 @@ class MultilineDocstringsAsWell:
and on so many lines...
"""


class SingleQuotedDocstring:
"I'm a docstring but I don't even get triple quotes."

0 comments on commit db9c592

Please sign in to comment.