Actually disable docstring prefix normalization with -S + fix instability (#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>
This commit is contained in:
Richard Si 2022-07-14 19:47:33 -04:00 committed by GitHub
parent 9aa33f467b
commit ad5c315dda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 4 deletions

View File

@ -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_

View File

@ -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]

View File

@ -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):

View File

@ -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.'''

View File

@ -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:
@ -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>
"""

View File

@ -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")