How to postpone/defer the evaluation of f-strings?

f-string, python, string-formatting, string-interpolation

Solution

Here's a complete "Ideal 2".

It's not an f-string—it doesn't even use f-strings—but it does as requested. Syntax exactly as specified. No security headaches since we are not using `eval()`.

It uses a little class and implements `__str__` which is automatically called by print. To escape the limited scope of the class we use the `inspect` module to hop one frame up and see the variables the caller has access to.

import inspect

class magic_fstring_function:
    def __init__(self, payload):
        self.payload = payload
    def __str__(self):
        vars = inspect.currentframe().f_back.f_globals.copy()
        vars.update(inspect.currentframe().f_back.f_locals)
        return self.payload.format(**vars)

template = "The current name is {name}"

template_a = magic_fstring_function(template)

# use it inside a function to demonstrate it gets the scoping right
def new_scope():
    names = ["foo", "bar"]
    for name in names:
        print(template_a)

new_scope()
# The current name is foo
# The current name is bar

Problem

I am using template strings to generate some files and I love the conciseness of the new f-strings for this purpose, for reducing my previous template code from something like this: ``` template_a = "The current name is {name}" names = ["foo", "bar"] for name in names: print (template_a.format(**locals())) ``` Now I can do this, directly replacing variables: ``` names = ["foo", "bar"] for name in names: print (f"The current name is {name}") ``` However, sometimes it makes sense to have the template defined elsewhere — higher up in the code, or imported from a file or something. This means the template is a static string with formatting tags in it. Something would have to happen to the string to tell the interpreter to interpret the string as a new f-string, but I don't know if there is such a thing. Is there any way to bring in a string and have it interpreted as an f-string to avoid using the `.format(**locals())` call? Ideally I want to be able to code like this... (where `magic_fstring_function` is where the part I don't understand comes in): ``` template_a = f"The current name is {name}" # OR [Ideal2] template_a = magic_fstring_function(open('template.txt').read()) names = ["foo", "bar"] for name in names: print (template_a) ``` ...with this desired output (without reading the file twice): ``` The current name is foo The current name is bar ``` ...but the actual output I get is: ``` The current name is {name} The current name is {name} ``` See also: How can I use f-string with a variable, not with a string literal?

Original source

Related problems