Repository: psf/black

Issue:
`--experimental-string-processing` crash on string + % {
```
% cat legacy.py                                   

def _legacy_listen_examples():
    text += (
        "    \"listen for the '%(event_name)s' event\"\n"
        "\n    # ... (event logic logic logic) ...\n"
        % {
            "since": since,
        }
    )
% black --experimental-string-processing legacy.py
error: cannot format legacy.py: Apparently, old parentheses do NOT exist?!
Oh no! 💥 💔 💥
1 file failed to reformat.
```
Minified from a file in sqlalchemy that we originally found this crash on.

The crash is in the `StringParenWrapper` class in `trans.py`, which apparently doesn't know how to handle the parentheses enclosing the string + `% {`. I spent some time looking at it but I'm not sure what the best solution is.


Discussion:
What even... apparently I opened the label selection menu at the worse time ever that I cleared the labels... AHHH
@bbugyi200 would you still be interested in fixing this one? Right now it's the only `--experimental-string-processing` crash we're aware of.

Resolving patch:
diff --git a/src/black/trans.py b/src/black/trans.py
--- a/src/black/trans.py
+++ b/src/black/trans.py
@@ -25,7 +25,7 @@
 from black.mode import Feature
 from black.nodes import syms, replace_child, parent_type
 from black.nodes import is_empty_par, is_empty_lpar, is_empty_rpar
-from black.nodes import CLOSING_BRACKETS, STANDALONE_COMMENT
+from black.nodes import OPENING_BRACKETS, CLOSING_BRACKETS, STANDALONE_COMMENT
 from black.lines import Line, append_leaves
 from black.brackets import BracketMatchError
 from black.comments import contains_pragma_comment
@@ -1398,6 +1398,11 @@ class StringParenWrapper(CustomSplitMapMixin, BaseStringSplitter):
     def do_splitter_match(self, line: Line) -> TMatchResult:
         LL = line.leaves
 
+        if line.leaves[-1].type in OPENING_BRACKETS:
+            return TErr(
+                "Cannot wrap parens around a line that ends in an opening bracket."
+            )
+
         string_idx = (
             self._return_match(LL)
             or self._else_match(LL)
@@ -1665,9 +1670,10 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
                 right_leaves.pop()
 
             if old_parens_exist:
-                assert (
-                    right_leaves and right_leaves[-1].type == token.RPAR
-                ), "Apparently, old parentheses do NOT exist?!"
+                assert right_leaves and right_leaves[-1].type == token.RPAR, (
+                    "Apparently, old parentheses do NOT exist?!"
+                    f" (left_leaves={left_leaves}, right_leaves={right_leaves})"
+                )
                 old_rpar_leaf = right_leaves.pop()
 
             append_leaves(string_line, line, right_leaves)


Test patch:
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -396,6 +396,16 @@ def xxxxxxx_xxxxxx(xxxx):
     " it has now"
 )
 
+
+def _legacy_listen_examples():
+    text += (
+        "    \"listen for the '%(event_name)s' event\"\n"
+        "\n    # ... (event logic logic logic) ...\n"
+        % {
+            "since": since,
+        }
+    )
+
 # output
 
 
@@ -886,3 +896,13 @@ def xxxxxxx_xxxxxx(xxxx):
     " it goes over 88 characters which"
     " it has now"
 )
+
+
+def _legacy_listen_examples():
+    text += (
+        "    \"listen for the '%(event_name)s' event\"\n"
+        "\n    # ... (event logic logic logic) ...\n"
+        % {
+            "since": since,
+        }
+    )