Skip to content

Commit

Permalink
Actually disable docstring prefix normalization with -S + fix instabi…
Browse files Browse the repository at this point in the history
…lity (psf#3168)

The former was a regression I introduced a long time ago. To avoid
changing the stable style too much, the regression is only fixed if
--preview is enabled

Annoyingly enough, as we currently always enforce a second format pass if
changes were made, there's no good way to prove the existence of the
docstring quote normalization instability issue. For posterity, here's
one failing example:

    --- source
    +++ first pass
    @@ -1,7 +1,7 @@
     def some_function(self):
    -    ''''<text here>
    +    """ '<text here>

         <text here, since without another non-empty line black is stable>

    -    '''
    +    """
         pass
    --- first pass
    +++ second pass
    @@ -1,7 +1,7 @@
     def some_function(self):
    -    """ '<text here>
    +    """'<text here>

         <text here, since without another non-empty line black is stable>

         """
         pass

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
  • Loading branch information
2 people authored and cibinmathew committed Aug 13, 2022
1 parent 70c5c7a commit 959ea36
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Expand Up @@ -19,6 +19,8 @@

- Single-character closing docstring quotes are no longer moved to their own line as
this is invalid. This was a bug introduced in version 22.6.0. (#3166)
- `--skip-string-normalization` / `-S` now prevents docstring prefixes from being
normalized as expected (#3168)

### _Blackd_

Expand Down
19 changes: 18 additions & 1 deletion src/black/linegen.py
Expand Up @@ -293,7 +293,24 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
if is_docstring(leaf) and "\\\n" not in leaf.value:
# We're ignoring docstrings with backslash newline escapes because changing
# indentation of those changes the AST representation of the code.
docstring = normalize_string_prefix(leaf.value)
if Preview.normalize_docstring_quotes_and_prefixes_properly in self.mode:
# There was a bug where --skip-string-normalization wouldn't stop us
# from normalizing docstring prefixes. To maintain stability, we can
# only address this buggy behaviour while the preview style is enabled.
if self.mode.string_normalization:
docstring = normalize_string_prefix(leaf.value)
# visit_default() does handle string normalization for us, but
# since this method acts differently depending on quote style (ex.
# see padding logic below), there's a possibility for unstable
# formatting as visit_default() is called *after*. To avoid a
# situation where this function formats a docstring differently on
# the second pass, normalize it early.
docstring = normalize_string_quotes(docstring)
else:
docstring = leaf.value
else:
# ... otherwise, we'll keep the buggy behaviour >.<
docstring = normalize_string_prefix(leaf.value)
prefix = get_string_prefix(docstring)
docstring = docstring[len(prefix) :] # Remove the prefix
quote_char = docstring[0]
Expand Down
7 changes: 4 additions & 3 deletions src/black/mode.py
Expand Up @@ -145,12 +145,13 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b
class Preview(Enum):
"""Individual preview style features."""

string_processing = auto()
remove_redundant_parens = auto()
one_element_subscript = auto()
annotation_parens = auto()
long_docstring_quotes_on_newline = auto()
normalize_docstring_quotes_and_prefixes_properly = auto()
one_element_subscript = auto()
remove_block_trailing_newline = auto()
remove_redundant_parens = auto()
string_processing = auto()


class Deprecated(UserWarning):
Expand Down
@@ -0,0 +1,10 @@
def do_not_touch_this_prefix():
R"""There was a bug where docstring prefixes would be normalized even with -S."""


def do_not_touch_this_prefix2():
F'There was a bug where docstring prefixes would be normalized even with -S.'


def do_not_touch_this_prefix3():
uR'''There was a bug where docstring prefixes would be normalized even with -S.'''
14 changes: 14 additions & 0 deletions tests/data/simple_cases/docstring.py
Expand Up @@ -209,6 +209,13 @@ def multiline_docstring_at_line_limit():
second line----------------------------------------------------------------------"""


def stable_quote_normalization_with_immediate_inner_single_quote(self):
''''<text here>
<text here, since without another non-empty line black is stable>
'''


# output

class MyClass:
Expand Down Expand Up @@ -417,3 +424,10 @@ def multiline_docstring_at_line_limit():
"""first line-----------------------------------------------------------------------
second line----------------------------------------------------------------------"""


def stable_quote_normalization_with_immediate_inner_single_quote(self):
"""'<text here>
<text here, since without another non-empty line black is stable>
"""
12 changes: 12 additions & 0 deletions tests/test_format.py
Expand Up @@ -139,6 +139,18 @@ def test_docstring_no_string_normalization() -> None:
assert_format(source, expected, mode)


def test_preview_docstring_no_string_normalization() -> None:
"""
Like test_docstring but with string normalization off *and* the preview style
enabled.
"""
source, expected = read_data(
"miscellaneous", "docstring_preview_no_string_normalization"
)
mode = replace(DEFAULT_MODE, string_normalization=False, preview=True)
assert_format(source, expected, mode)


def test_long_strings_flag_disabled() -> None:
"""Tests for turning off the string processing logic."""
source, expected = read_data("miscellaneous", "long_strings_flag_disabled")
Expand Down

0 comments on commit 959ea36

Please sign in to comment.