Repository: asottile/pyupgrade

Issue:
pyupgrade on f-strings with .format() results in invalid f-string
I understand that this is an edge-case because the input was buggy, but it _does_ convert running code into non-running code.

```
$ cat a.py
print(f"foo {0}".format("bar"))
$ python a.py
foo 0
$ pyupgrade a.py
Rewriting a.py
$ cat a.py
print(f"foo {}".format("bar"))
$ python a.py
  File "a.py", line 1
    print(f"foo {}".format("bar"))
         ^
SyntaxError: f-string: empty expression not allowed
```

```
$ pip freeze | grep pyupgrade
pyupgrade==2.7.0
```

I'm not sure what the right behavior is in this case; maybe just don't touch numbered args inside an f-string?

Discussion:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index c463fcf..54bbe0d 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -587,6 +587,11 @@ def _fix_format_literal(tokens: List[Token], end: int) -> None:
     parsed_parts = []
     last_int = -1
     for i in parts:
+        # f'foo {0}'.format(...) would get turned into a SyntaxError
+        prefix, _ = parse_string_literal(tokens[i].src)
+        if 'f' in prefix.lower():
+            return
+
         try:
             parsed = parse_format(tokens[i].src)
         except ValueError:


Test patch:
diff --git a/tests/format_literals_test.py b/tests/format_literals_test.py
index f3610a1..0719138 100644
--- a/tests/format_literals_test.py
+++ b/tests/format_literals_test.py
@@ -51,6 +51,8 @@ def test_intentionally_not_round_trip(s, expected):
         '("{0}" # {1}\n"{2}").format(1, 2, 3)',
         # TODO: this works by accident (extended escape treated as placeholder)
         r'"\N{snowman} {}".format(1)',
+        # don't touch f-strings (these are wrong but don't make it worse)
+        'f"{0}".format(a)',
     ),
 )
 def test_format_literals_noop(s):