Repository: asottile/pyupgrade

Issue:
KeyError running with --py36-plus on matplotlib's ticker.py
Running pyupgrade 1.5 on matplotlib's ticker.py as of, say, mpl3.0rc2 (https://github.com/matplotlib/matplotlib/blob/v3.0.0rc2/lib/matplotlib/ticker.py) gives me
```
Traceback (most recent call last):
  File "/usr/bin/pyupgrade", line 11, in <module>
    sys.exit(main())
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 907, in main
    ret |= fix_file(filename, args)
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 884, in fix_file
    contents_text = _fix_fstrings(contents_text)
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 858, in _fix_fstrings
    tokens[i] = token._replace(src=_to_fstring(token.src, node))
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 824, in _to_fstring
    name = ''.join((params[k or str(i)], dot, rest))
KeyError: '3'
```

Discussion:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index 486a440..b9cb902 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -818,10 +818,12 @@ def _to_fstring(src, call):
         params[kwd.arg] = _unparse(kwd.value)
 
     parts = []
-    for i, (s, name, spec, conv) in enumerate(parse_format('f' + src)):
+    i = 0
+    for s, name, spec, conv in parse_format('f' + src):
         if name is not None:
             k, dot, rest = name.partition('.')
             name = ''.join((params[k or str(i)], dot, rest))
+            i += 1
         parts.append((s, name, spec, conv))
     return unparse_parsed_string(parts)
 


Test patch:
diff --git a/tests/pyupgrade_test.py b/tests/pyupgrade_test.py
index 80d0b3b..24b47f4 100644
--- a/tests/pyupgrade_test.py
+++ b/tests/pyupgrade_test.py
@@ -721,6 +721,7 @@ def test_fix_fstrings_noop(s):
         ('"{.x} {.y}".format(a, b)', 'f"{a.x} {b.y}"'),
         ('"{} {}".format(a.b, c.d)', 'f"{a.b} {c.d}"'),
         ('"hello {}!".format(name)', 'f"hello {name}!"'),
+        ('"{}{{}}{}".format(escaped, y)', 'f"{escaped}{{}}{y}"'),
 
         # TODO: poor man's f-strings?
         # '"{foo}".format(**locals())'