Skip to content

Instantly share code, notes, and snippets.

@dirkf
Last active February 20, 2023 15:06
Show Gist options
  • Save dirkf/26c1dc0e0f29e2663c8cfd58f932aca6 to your computer and use it in GitHub Desktop.
Save dirkf/26c1dc0e0f29e2663c8cfd58f932aca6 to your computer and use it in GitHub Desktop.
Downgrade f-strings (thanks https://github.com/JohannesBuchner/fstring-downgrade for inspiration and original code)
#!/usr/bin/env python
import sys
import re
inp = sys.stdin
out = sys.stdout
linestart = 0
while True:
if linestart == 0:
line = next(inp, None)
if line is None:
break
else:
line = line[linestart:]
while re.search(r'''(?:'|")\s*$''', line):
nextl = next(inp, None)
if nextl is None:
break
line += nextl
m = re.search(r'''(?i)(?:(?P<r>rf|fr)|f)(?P<q>'|")(?:(?(r)\\|\\\\)(?P=q)|(?P=q)\s*f?(?P=q)|(?!(?P=q)).)*(?P=q)''', line)
if not m:
out.write(line)
linestart = 0
continue
try:
out.write(line[:m.start(0)])
start = line[m.start(0):m.end('q')]
part = line[m.start('q'):m.end(0)]
q = m.group('q')
linestart = m.end(0)
keys = [m.group(1) for m in re.finditer(r'{([^}:]*)(:[^}]*)?}', part)]
n = [0]
def replacer(m):
r = '{%d%s}' % (n[0], m.group(2))
n[0] += 1
return r
part = re.sub(r'{([^}:]*)((:[^}]*)?)}', replacer, part)
part = re.sub(q + r'(\s*)f' + q, q + r'\1' + q, part)
out.write(start.replace('f', ''))
out.write(part[1:])
if len(keys) > 0:
out.write('.format(' + ', '.join(['%s' % (key) for key in keys]) + ')')
except Exception as e:
print(e)
print("skipping problematic line: '%s'" % line.rstrip())
out.write(line)
linestart = 0
@dirkf
Copy link
Author

dirkf commented Dec 6, 2022

stdin->stdout filter generates numbered '...{n}...' format items from f-strings for 2.6 compat.

Issues:

  • won't handle literal f-string concatenation with different quoting
  • only handles shortstrings ('...', "...", not '''...''', """...""")
  • also confused by ! formatting tweaks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment