Repository: psf/black

Issue:
Black produced invalid code: invalid syntax with --experimental-string-processing
I hope this helps to make the experimental string processing a bit less experimental :-)

## Environment
Python 3.8.6 
black, version 20.8b1
Separate venv (via pipx) where only black and its' dependencies are installed

## Executed Command
`black myfile.py --experimental-string-processing`

## myfile.py
```python
print(f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}")
```

## Error Mesage

```
black myfile.py --experimental-string-processing
error: cannot format myfile.py: INTERNAL ERROR: Black produced invalid code: invalid syntax (<unknown>, line 77). Please report a bug on https://github.com/psf/black/issues.  This invalid output might be helpful: /tmp/blk_tqh1ju35.log
Oh no! 💥 💔 💥
1 file failed to reformat.

Process finished with exit code 123
```

## Log file
```
  File "[..]/python3.8/site-packages/black/__init__.py", line 6141, in assert_equivalent
    dst_ast = parse_ast(dst)
  File "[..]/python3.8/site-packages/black/__init__.py", line 6052, in parse_ast
    return ast27.parse(src)
  File "[..]/python3.8/site-packages/typed_ast/ast27.py", line 50, in parse
    return _ast27.parse(source, filename, mode)
print(
    f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam {[f'{i}'"
    " for i in range(10)]}"
)
```


Discussion:
Looks like we should make it so it doesn't try to break the string in the middle of an f-string expression part. cc @bbugyi200.

Resolving patch:
diff --git a/src/black/__init__.py b/src/black/__init__.py
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -3611,13 +3611,14 @@ class StringSplitter(CustomSplitMapMixin, BaseStringSplitter):
     MIN_SUBSTR_SIZE = 6
     # Matches an "f-expression" (e.g. {var}) that might be found in an f-string.
     RE_FEXPR = r"""
-    (?<!\{)\{
+    (?<!\{) (?:\{\{)* \{ (?!\{)
         (?:
             [^\{\}]
             | \{\{
             | \}\}
+            | (?R)
         )+?
-    (?<!\})(?:\}\})*\}(?!\})
+    (?<!\}) \} (?:\}\})* (?!\})
     """
 
     def do_splitter_match(self, line: Line) -> TMatchResult:


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
@@ -371,6 +371,10 @@ def xxxxxxx_xxxxxx(xxxx):
     ),
 }
 
+# We do NOT split on f-string expressions.
+print(f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}")
+x = f"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}."
+
 # output
 
 
@@ -830,3 +834,13 @@ def xxxxxxx_xxxxxx(xxxx):
         r"(?<!([0-9]\ ))(?<=(^|\ ))([A-Z]+(\ )?|[0-9](\ )|[a-z](\\\ )){4,7}([A-Z]|[0-9]|[a-z])($|\b)(?!(\ ?([0-9]\ )|(\.)))"
     ),
 }
+
+# We do NOT split on f-string expressions.
+print(
+    "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam."
+    f" {[f'{i}' for i in range(10)]}"
+)
+x = (
+    "This is a long string which contains an f-expr that should not split"
+    f" {{{[i for i in range(5)]}}}."
+)