]> git.madduck.net Git - etc/vim.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Wrap concatenated strings used as function args in parens (#3307)
authorYilei "Dolee" Yang <yileiyang@google.com>
Thu, 27 Oct 2022 01:03:10 +0000 (18:03 -0700)
committerGitHub <noreply@github.com>
Thu, 27 Oct 2022 01:03:10 +0000 (18:03 -0700)
Fixes #3292

CHANGES.md
src/black/__init__.py
src/black/mode.py
src/black/parsing.py
src/black/trans.py
tests/data/preview/cantfit.py
tests/data/preview/long_strings.py
tests/data/preview/long_strings__regression.py
tests/test_black.py

index 67451f7caf56335b9b85ef4120c27e805aa489d7..4db961149bfb23649660114bc8fbb1ccaeeb3816 100644 (file)
@@ -15,6 +15,8 @@
 <!-- Changes that affect Black's preview style -->
 
 - Enforce empty lines before classes and functions with sticky leading comments (#3302)
 <!-- Changes that affect Black's preview style -->
 
 - Enforce empty lines before classes and functions with sticky leading comments (#3302)
+- Implicitly concatenated strings used as function args are now wrapped inside
+  parentheses (#3307)
 
 ### Configuration
 
 
 ### Configuration
 
index d9fba41ebd34136560e09ae296e1a4a425dec8f5..94592278c315d35093f34ddce43eddd866605eea 100644 (file)
@@ -497,8 +497,10 @@ def main(  # noqa: C901
             user_level_config = str(find_user_pyproject_toml())
             if config == user_level_config:
                 out(
             user_level_config = str(find_user_pyproject_toml())
             if config == user_level_config:
                 out(
-                    "Using configuration from user-level config at "
-                    f"'{user_level_config}'.",
+                    (
+                        "Using configuration from user-level config at "
+                        f"'{user_level_config}'."
+                    ),
                     fg="blue",
                 )
             elif config_source in (
                     fg="blue",
                 )
             elif config_source in (
index 1e83f2a9c6dbc2f38663b58e2f237c21ec3d66be..e2eff2391b14d290947f3c532aab364ebb51fcc0 100644 (file)
@@ -180,8 +180,10 @@ class Mode:
     def __post_init__(self) -> None:
         if self.experimental_string_processing:
             warn(
     def __post_init__(self) -> None:
         if self.experimental_string_processing:
             warn(
-                "`experimental string processing` has been included in `preview`"
-                " and deprecated. Use `preview` instead.",
+                (
+                    "`experimental string processing` has been included in `preview`"
+                    " and deprecated. Use `preview` instead."
+                ),
                 Deprecated,
             )
 
                 Deprecated,
             )
 
index 762934b8869c26c5c08d5f41336651eaac743be5..c37c12b868d7f5e75e73e6f036ea7d7d5bae3604 100644 (file)
@@ -29,9 +29,11 @@ try:
 except ImportError:
     if sys.version_info < (3, 8) and not _IS_PYPY:
         print(
 except ImportError:
     if sys.version_info < (3, 8) and not _IS_PYPY:
         print(
-            "The typed_ast package is required but not installed.\n"
-            "You can upgrade to Python 3.8+ or install typed_ast with\n"
-            "`python3 -m pip install typed-ast`.",
+            (
+                "The typed_ast package is required but not installed.\n"
+                "You can upgrade to Python 3.8+ or install typed_ast with\n"
+                "`python3 -m pip install typed-ast`."
+            ),
             file=sys.stderr,
         )
         sys.exit(1)
             file=sys.stderr,
         )
         sys.exit(1)
index 7e2d8e67c1acc1ffee6196c9d822bfe9936ad66b..8893ab02aab8df61d670dcb6d148bd99647b43d6 100644 (file)
@@ -1058,33 +1058,19 @@ class BaseStringSplitter(StringTransformer):
         if LL[0].type != token.STRING:
             return None
 
         if LL[0].type != token.STRING:
             return None
 
-        matching_nodes = [
-            syms.listmaker,
-            syms.dictsetmaker,
-            syms.testlist_gexp,
-        ]
-        # If the string is an immediate child of a list/set/tuple literal...
-        if (
-            parent_type(LL[0]) in matching_nodes
-            or parent_type(LL[0].parent) in matching_nodes
+        # If the string is surrounded by commas (or is the first/last child)...
+        prev_sibling = LL[0].prev_sibling
+        next_sibling = LL[0].next_sibling
+        if not prev_sibling and not next_sibling and parent_type(LL[0]) == syms.atom:
+            # If it's an atom string, we need to check the parent atom's siblings.
+            parent = LL[0].parent
+            assert parent is not None  # For type checkers.
+            prev_sibling = parent.prev_sibling
+            next_sibling = parent.next_sibling
+        if (not prev_sibling or prev_sibling.type == token.COMMA) and (
+            not next_sibling or next_sibling.type == token.COMMA
         ):
         ):
-            # And the string is surrounded by commas (or is the first/last child)...
-            prev_sibling = LL[0].prev_sibling
-            next_sibling = LL[0].next_sibling
-            if (
-                not prev_sibling
-                and not next_sibling
-                and parent_type(LL[0]) == syms.atom
-            ):
-                # If it's an atom string, we need to check the parent atom's siblings.
-                parent = LL[0].parent
-                assert parent is not None  # For type checkers.
-                prev_sibling = parent.prev_sibling
-                next_sibling = parent.next_sibling
-            if (not prev_sibling or prev_sibling.type == token.COMMA) and (
-                not next_sibling or next_sibling.type == token.COMMA
-            ):
-                return 0
+            return 0
 
         return None
 
 
         return None
 
@@ -1653,9 +1639,8 @@ class StringParenWrapper(BaseStringSplitter, CustomSplitMapMixin):
         assigned the value of some string.
             OR
         * The line starts with an "atom" string that prefers to be wrapped in
         assigned the value of some string.
             OR
         * The line starts with an "atom" string that prefers to be wrapped in
-        parens. It's preferred to be wrapped when it's is an immediate child of
-        a list/set/tuple literal, AND the string is surrounded by commas (or is
-        the first/last child).
+        parens. It's preferred to be wrapped when the string is surrounded by
+        commas (or is the first/last child).
 
     Transformations:
         The chosen string is wrapped in parentheses and then split at the LPAR.
 
     Transformations:
         The chosen string is wrapped in parentheses and then split at the LPAR.
index 0849374f776ebbefc13f0332dd55aaeb0e690feb..cade382e30d8cff6de35c8dbfd0f1ceeba43c006 100644 (file)
@@ -79,10 +79,14 @@ normal_name = (
 )
 # long arguments
 normal_name = normal_function_name(
 )
 # long arguments
 normal_name = normal_function_name(
-    "but with super long string arguments that on their own exceed the line limit so"
-    " there's no way it can ever fit",
-    "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs"
-    " with spam and eggs and spam with eggs",
+    (
+        "but with super long string arguments that on their own exceed the line limit"
+        " so there's no way it can ever fit"
+    ),
+    (
+        "eggs with spam and eggs and spam with eggs with spam and eggs and spam with"
+        " eggs with spam and eggs and spam with eggs"
+    ),
     this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0,
 )
 string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do"  # noqa
     this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0,
 )
 string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do"  # noqa
index 6db3cfed9a92c3b145ec97a56e5005507013d857..9288b253b60e4c1950af3b0368a18861745bc281 100644 (file)
@@ -297,8 +297,10 @@ x += (
 y = "Short string"
 
 print(
 y = "Short string"
 
 print(
-    "This is a really long string inside of a print statement with extra arguments"
-    " attached at the end of it.",
+    (
+        "This is a really long string inside of a print statement with extra arguments"
+        " attached at the end of it."
+    ),
     x,
     y,
     z,
     x,
     y,
     z,
@@ -474,13 +476,15 @@ bad_split3 = (
 )
 
 bad_split_func1(
 )
 
 bad_split_func1(
-    "But what should happen when code has already "
-    "been formatted but in the wrong way? Like "
-    "with a space at the end instead of the "
-    "beginning. Or what about when it is split too "
-    "soon? In the case of a split that is too "
-    "short, black will try to honer the custom "
-    "split.",
+    (
+        "But what should happen when code has already "
+        "been formatted but in the wrong way? Like "
+        "with a space at the end instead of the "
+        "beginning. Or what about when it is split too "
+        "soon? In the case of a split that is too "
+        "short, black will try to honer the custom "
+        "split."
+    ),
     xxx,
     yyy,
     zzz,
     xxx,
     yyy,
     zzz,
@@ -583,9 +587,11 @@ comment_string = (  # This comment gets thrown to the top.
 )
 
 arg_comment_string = print(
 )
 
 arg_comment_string = print(
-    "Long lines with inline comments which are apart of (and not the only member of) an"
-    " argument list should have their comments appended to the reformatted string's"
-    " enclosing left parentheses.",  # This comment gets thrown to the top.
+    (  # This comment gets thrown to the top.
+        "Long lines with inline comments which are apart of (and not the only member"
+        " of) an argument list should have their comments appended to the reformatted"
+        " string's enclosing left parentheses."
+    ),
     "Arg #2",
     "Arg #3",
     "Arg #4",
     "Arg #2",
     "Arg #3",
     "Arg #4",
@@ -645,23 +651,31 @@ return (
 )
 
 func_with_bad_comma(
 )
 
 func_with_bad_comma(
-    "This is a really long string argument to a function that has a trailing comma"
-    " which should NOT be there.",
+    (
+        "This is a really long string argument to a function that has a trailing comma"
+        " which should NOT be there."
+    ),
 )
 
 func_with_bad_comma(
 )
 
 func_with_bad_comma(
-    "This is a really long string argument to a function that has a trailing comma"
-    " which should NOT be there.",  # comment after comma
+    (  # comment after comma
+        "This is a really long string argument to a function that has a trailing comma"
+        " which should NOT be there."
+    ),
 )
 
 func_with_bad_comma(
 )
 
 func_with_bad_comma(
-    "This is a really long string argument to a function that has a trailing comma"
-    " which should NOT be there.",
+    (
+        "This is a really long string argument to a function that has a trailing comma"
+        " which should NOT be there."
+    ),
 )
 
 func_with_bad_comma(
 )
 
 func_with_bad_comma(
-    "This is a really long string argument to a function that has a trailing comma"
-    " which should NOT be there.",  # comment after comma
+    (  # comment after comma
+        "This is a really long string argument to a function that has a trailing comma"
+        " which should NOT be there."
+    ),
 )
 
 func_with_bad_parens_that_wont_fit_in_one_line(
 )
 
 func_with_bad_parens_that_wont_fit_in_one_line(
index 634db46a5e078319af8d9c8f678f255a431e1bc1..8b00e76f40e5c7f9e8248a519c2aae3a9c2a48a6 100644 (file)
@@ -679,9 +679,11 @@ class A:
     def foo():
         some_func_call(
             "xxxxxxxxxx",
     def foo():
         some_func_call(
             "xxxxxxxxxx",
-            "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x "
-            '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; '
-            "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ",
+            (
+                "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x "
+                '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; '
+                "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" "
+            ),
             None,
             ("xxxxxxxxxxx",),
         ),
             None,
             ("xxxxxxxxxxx",),
         ),
@@ -690,9 +692,11 @@ class A:
 class A:
     def foo():
         some_func_call(
 class A:
     def foo():
         some_func_call(
-            "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x "
-            "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; "
-            "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ",
+            (
+                "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x "
+                "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; "
+                "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" "
+            ),
             None,
             ("xxxxxxxxxxx",),
         ),
             None,
             ("xxxxxxxxxxx",),
         ),
@@ -810,8 +814,10 @@ some_commented_string = (
 )
 
 lpar_and_rpar_have_comments = func_call(  # LPAR Comment
 )
 
 lpar_and_rpar_have_comments = func_call(  # LPAR Comment
-    "Long really ridiculous type of string that shouldn't really even exist at all. I"
-    " mean commmme onnn!!!",  # Comma Comment
+    (  # Comma Comment
+        "Long really ridiculous type of string that shouldn't really even exist at all."
+        " I mean commmme onnn!!!"
+    ),
 )  # RPAR Comment
 
 cmd_fstring = (
 )  # RPAR Comment
 
 cmd_fstring = (
index 5d0175d9d66488803870c5d412102a57e29622fb..9256698ea2792eb49ba559febe803afde73b9d60 100644 (file)
@@ -490,8 +490,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
             self.assertEqual(
                 unstyle(str(report)),
-                "1 file reformatted, 2 files left unchanged, 1 file failed to"
-                " reformat.",
+                (
+                    "1 file reformatted, 2 files left unchanged, 1 file failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f3"), black.Changed.YES)
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f3"), black.Changed.YES)
@@ -500,8 +502,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(out_lines[-1], "reformatted f3")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(out_lines[-1], "reformatted f3")
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 1 file failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 1 file failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.failed(Path("e2"), "boom")
             )
             self.assertEqual(report.return_code, 123)
             report.failed(Path("e2"), "boom")
@@ -510,8 +514,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.path_ignored(Path("wat"), "no match")
             )
             self.assertEqual(report.return_code, 123)
             report.path_ignored(Path("wat"), "no match")
@@ -520,8 +526,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(out_lines[-1], "wat ignored: no match")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(out_lines[-1], "wat ignored: no match")
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f4"), black.Changed.NO)
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f4"), black.Changed.NO)
@@ -530,22 +538,28 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 3 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 3 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.check = True
             self.assertEqual(
                 unstyle(str(report)),
             )
             self.assertEqual(report.return_code, 123)
             report.check = True
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files would be reformatted, 3 files would be left unchanged, 2"
-                " files would fail to reformat.",
+                (
+                    "2 files would be reformatted, 3 files would be left unchanged, 2"
+                    " files would fail to reformat."
+                ),
             )
             report.check = False
             report.diff = True
             self.assertEqual(
                 unstyle(str(report)),
             )
             report.check = False
             report.diff = True
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files would be reformatted, 3 files would be left unchanged, 2"
-                " files would fail to reformat.",
+                (
+                    "2 files would be reformatted, 3 files would be left unchanged, 2"
+                    " files would fail to reformat."
+                ),
             )
 
     def test_report_quiet(self) -> None:
             )
 
     def test_report_quiet(self) -> None:
@@ -587,8 +601,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
             self.assertEqual(
                 unstyle(str(report)),
-                "1 file reformatted, 2 files left unchanged, 1 file failed to"
-                " reformat.",
+                (
+                    "1 file reformatted, 2 files left unchanged, 1 file failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f3"), black.Changed.YES)
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f3"), black.Changed.YES)
@@ -596,8 +612,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(len(err_lines), 1)
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(len(err_lines), 1)
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 1 file failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 1 file failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.failed(Path("e2"), "boom")
             )
             self.assertEqual(report.return_code, 123)
             report.failed(Path("e2"), "boom")
@@ -606,8 +624,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.path_ignored(Path("wat"), "no match")
             )
             self.assertEqual(report.return_code, 123)
             report.path_ignored(Path("wat"), "no match")
@@ -615,8 +635,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f4"), black.Changed.NO)
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f4"), black.Changed.NO)
@@ -624,22 +646,28 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 3 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 3 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.check = True
             self.assertEqual(
                 unstyle(str(report)),
             )
             self.assertEqual(report.return_code, 123)
             report.check = True
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files would be reformatted, 3 files would be left unchanged, 2"
-                " files would fail to reformat.",
+                (
+                    "2 files would be reformatted, 3 files would be left unchanged, 2"
+                    " files would fail to reformat."
+                ),
             )
             report.check = False
             report.diff = True
             self.assertEqual(
                 unstyle(str(report)),
             )
             report.check = False
             report.diff = True
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files would be reformatted, 3 files would be left unchanged, 2"
-                " files would fail to reformat.",
+                (
+                    "2 files would be reformatted, 3 files would be left unchanged, 2"
+                    " files would fail to reformat."
+                ),
             )
 
     def test_report_normal(self) -> None:
             )
 
     def test_report_normal(self) -> None:
@@ -683,8 +711,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
             self.assertEqual(
                 unstyle(str(report)),
-                "1 file reformatted, 2 files left unchanged, 1 file failed to"
-                " reformat.",
+                (
+                    "1 file reformatted, 2 files left unchanged, 1 file failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f3"), black.Changed.YES)
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f3"), black.Changed.YES)
@@ -693,8 +723,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(out_lines[-1], "reformatted f3")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(out_lines[-1], "reformatted f3")
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 1 file failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 1 file failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.failed(Path("e2"), "boom")
             )
             self.assertEqual(report.return_code, 123)
             report.failed(Path("e2"), "boom")
@@ -703,8 +735,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.path_ignored(Path("wat"), "no match")
             )
             self.assertEqual(report.return_code, 123)
             report.path_ignored(Path("wat"), "no match")
@@ -712,8 +746,10 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 2 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 2 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f4"), black.Changed.NO)
             )
             self.assertEqual(report.return_code, 123)
             report.done(Path("f4"), black.Changed.NO)
@@ -721,22 +757,28 @@ class BlackTestCase(BlackBaseTestCase):
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
             self.assertEqual(len(err_lines), 2)
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files reformatted, 3 files left unchanged, 2 files failed to"
-                " reformat.",
+                (
+                    "2 files reformatted, 3 files left unchanged, 2 files failed to"
+                    " reformat."
+                ),
             )
             self.assertEqual(report.return_code, 123)
             report.check = True
             self.assertEqual(
                 unstyle(str(report)),
             )
             self.assertEqual(report.return_code, 123)
             report.check = True
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files would be reformatted, 3 files would be left unchanged, 2"
-                " files would fail to reformat.",
+                (
+                    "2 files would be reformatted, 3 files would be left unchanged, 2"
+                    " files would fail to reformat."
+                ),
             )
             report.check = False
             report.diff = True
             self.assertEqual(
                 unstyle(str(report)),
             )
             report.check = False
             report.diff = True
             self.assertEqual(
                 unstyle(str(report)),
-                "2 files would be reformatted, 3 files would be left unchanged, 2"
-                " files would fail to reformat.",
+                (
+                    "2 files would be reformatted, 3 files would be left unchanged, 2"
+                    " files would fail to reformat."
+                ),
             )
 
     def test_lib2to3_parse(self) -> None:
             )
 
     def test_lib2to3_parse(self) -> None: