Skip to content

Instantly share code, notes, and snippets.

@yyogo
Last active October 28, 2019 15:55
Show Gist options
  • Save yyogo/3e984bdf537b948755d66981a30e4c9d to your computer and use it in GitHub Desktop.
Save yyogo/3e984bdf537b948755d66981a30e4c9d to your computer and use it in GitHub Desktop.
""backported"" string-interpolation for Python 2/3.5- (What do you mean, 'why'? Why not?)
import inspect
try:
unichr
except NameError:
unichr = chr
def f(s):
# find all {}'s
stack = []
ranges = []
if isinstance(s, bytes):
s = s.decode('utf-8')
iterator = iter(enumerate(s))
for i, c in iterator:
# escape
if s[i:i+2] == u'}}' or s[i:i+2] == u'{{':
iterator.next()
continue
elif c == u'{':
stack.insert(0, i)
elif c == u'}':
if not stack:
raise SyntaxError("f-string: single '}' is not allowed")
ranges.append((stack.pop(0)+1, i))
if stack:
raise SyntaxError("f-string: expecting '}'")
expressions = {}
for i, (start, stop) in enumerate(ranges):
interp = s[start:stop]
expr = interp
if u':' in interp:
expr = interp[:interp.index(u':')]
if u'!' in interp:
expr = interp[:interp.index(u'!')]
key = unichr(i+1).ljust(len(expr))
expressions[key] = interp[:len(expr)]
interp = key + interp[len(expr):]
s = s[:start] + interp + s[stop:]
values = {}
frame = inspect.getouterframes(inspect.currentframe())[1][0]
for key, expr in expressions.items():
# evalueate expresions in original stack frame
#print('eval ', repr(expr))
val = eval(expr, frame.f_globals, frame.f_locals)
values[key] = val
return s.format(**values)
if __name__ == "__main__":
a = 'world'
import math, os
print(f("hello {a}! i'm {os.environ['USER']}. 2+2 is {2+2}, and the square root of pi is {math.sqrt(math.pi)}. \n{{expressions}} in f-strings work!"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment