Skip to content Skip to sidebar Skip to footer

Trigger F-string Parse On Python String In Variable

This question comes from handling jupyter magics, but can be expressed in a more simple way. Given a string s = 'the key is {d['key']}' and a dictionary d = {'key': 'val'}, we want

Solution 1:

String formatting can handle most string dictionary keys just fine, but you need to remove the quotes:

"the key is {d[key]}".format(d=d)

Demo:

>>>d = {'key': 'val'}>>>"the key is {d[key]}".format(d=d)
'the key is val'

str.format() syntax isn't quite the same thing as Python expression syntax (which is what f-strings mostly support).

From the Format String Syntax documentation:

field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
[...]
element_index     ::=  digit+ | index_string
index_string      ::=  <any source character except "]"> +

and

[A]n expression of the form '[index]' does an index lookup using __getitem__()

The syntax is limited, in that it will convert any digit-only strings into an integer, and everything else is always interpreted as a string (though you could use nested {} placeholders to dynamically interpolate a key value from another variable).

If you must support arbitrary expressions, the same way that f-strings do and you do not take template strings from untrusted sources (this part is important), then you could parse out the field name components and then use the eval() function to evaluate the values before you then output the final string:

from string import Formatter

_conversions = {'a': ascii, 'r': repr, 's': str}

defevaluate_template_expressions(template, globals_=None):
    if globals_ isNone:
        globals_ = globals()
    result = []
    parts = Formatter().parse(template)
    for literal_text, field_name, format_spec, conversion in parts:
        if literal_text:
            result.append(literal_text)
        ifnot field_name:
            continue
        value = eval(field_name, globals_)
        if conversion:
            value = _conversions[conversion](value)
        if format_spec:
            value = format(value, format_spec)
        result.append(value)
    return''.join(result)

Now the quotes are accepted:

>>>s = "the key is {d['key']}">>>d = {'key': 'val'}>>>evaluate_template_expressions(s)
'the key is val'

Essentially, you can do the same with eval(f'f{s!r}', globals()), but the above might give you some more control over what expressions you might want to support.

Solution 2:

[G]iven s and d, how do you force a f'string' parse of s? Is there some function or method available?

This can be done... using eval. But beware eval!

>>> eval('f' + repr(s))
the key is val

The repr is there to escape any quotes and to wrap s itself with quotes.

If you are aware of which variables to format (d in this case), opt for Martijn's answer of doing str.format. The above solution should be your last resort due to the dangers of eval.

Post a Comment for "Trigger F-string Parse On Python String In Variable"