diff --git a/CHANGES.md b/CHANGES.md index 09954f2b738..3a4d8fdf17e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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_ diff --git a/src/black/linegen.py b/src/black/linegen.py index 20f3ac6fffb..1f132b7888f 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -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] diff --git a/src/black/mode.py b/src/black/mode.py index 896c516df79..b7359fab213 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -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): diff --git a/tests/data/miscellaneous/docstring_preview_no_string_normalization.py b/tests/data/miscellaneous/docstring_preview_no_string_normalization.py new file mode 100644 index 00000000000..0957231eb9c --- /dev/null +++ b/tests/data/miscellaneous/docstring_preview_no_string_normalization.py @@ -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.''' diff --git a/tests/data/simple_cases/docstring.py b/tests/data/simple_cases/docstring.py index 7153be468c1..f08bba575fe 100644 --- a/tests/data/simple_cases/docstring.py +++ b/tests/data/simple_cases/docstring.py @@ -209,6 +209,13 @@ def multiline_docstring_at_line_limit(): second line----------------------------------------------------------------------""" +def stable_quote_normalization_with_immediate_inner_single_quote(self): + '''' + + + ''' + + # 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): + """' + + + """ diff --git a/tests/test_format.py b/tests/test_format.py index 0e1059c61e4..86339f24b86 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -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")