- 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_
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]
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):
--- /dev/null
+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.'''
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:
"""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>
+ """
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")