ActiveState Code

Recipe 496885: String Interpolation with Evaluation of Embedded Expressions


A lightweight and powerful way to evaluate expressions embedded in strings during interpolation.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class InterpolationEvaluationException(KeyError):
    pass 

class expression_dictionary(dict):
    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            try:
                return eval(key,self)
            except Exception, e:
                raise InterpolationEvaluationException(key, e)


# ---------- Usage ---------------

# Evaluate expressions in the context of a dictionary...
>>> my_dict = {'x': 1, 'y': 2}
>>> print "The sum of %(x)s and %(y)s is %(x+y)s" % expression_dictionary(my_dict)
  The sum of 1 and 2 is 3

# or use in conjunction with locals() or globals() to evaluate in a namespace.
>>> ft = 14410.0
>>> ns = expression_dictionary(locals())
>>> print "  Summit altitude: %(ft)0.1f feet (%(ft * 0.3048)0.1f meters)" % ns
  Summit altitude: 14410.0 feet (4392.2 meters)

Discussion

Standard Python string interpolation allows elements of a dictionary to be interpolated with the "%(key)F" syntax, where F is a format code. Hence, the expression:

"Hello, %(greetee)s." % {'greetee':'WORLD'}

evaluates as =>

"Hello, WORLD."

It is often desirable to manipulate the contents of the dictionary in some way before interpolating. The usual approach is to make another key/value pair containing the modified value:

d = {'greetee':'WORLD'} d['lower_case_greetee'] = d['greetee'].lower() "Hello, %(lower_case_greetee)s." % d

=>

"Hello, world."

But this is tedious. Wouldn't it be nice to perform inline manipulations immediately before interpolating?

"Hello, %(greetee.lower())s." % {'greetee':'WORLD'}

=> "Hello, world."


Well, now you can. Hooray. Any dictionary-like object, wrapped in the expression_dictionary class, will run eval() on expressions.

The advanced Pythonistas in the audience might point out, quite correctly, that the expression_dictionary class actually has nothing to do specifically with string interpolation. Indeed, it could be used for anything...

>>> d = evaluation_dictionary()
>>> d['x'] = 10
>>> d['y'] = 20
>>> print d['x + y']
30

... but the author feels it is most useful for interpolation.

Advantages: + Uses standard string interpolation syntax + Does not require modified string classes + Simple + Adequate for most light-weight templating needs

Disadvantages: - May be slow if used with large namespaces - Insecure by design (because it uses eval)... NEVER allow arbitrary or untrusted expressions to be evaluated,e.g. %(__import__('os').remove('/my/file'))s - Slower than a dict for normal keyword lookup (because they go through eval)

Sign in to comment