mszep
mszep

Reputation: 420

eval in template strings

I'm considering porting a rather unwieldy bash script to python but I'm stuck on how to handle the following aspect: The point of the script is to generate a png image depending on dynamically fetched data. The bash script grabs the data, and builds a very long invocation of the convert utility, with lots of options. It seemed like python's template strings would be a good solution (I would vastly prefer to stay within the standard library, since I'll be deploying to shared hosting), but I discovered that you can't evaluate expressions as you can in bash:

>>> from string import Template
>>> s = Template('The width times one is ${width}')
>>> s.substitute(width=45)
'The width times one is 45'
>>> t = Template('The width times two is ${width*2}')
>>> t.substitute(width=45)
# Raises ValueError

Since my bash script depends quite heavily on such arithmetic (otherwise the number of variables to keep track of would increase exponentially) I'd like to know if there's a way to emulate this behavior in python. I saw that this question, asking roughly the same, has a comment, reading:

This would be very unPythonic, because it's counterintuitive -- strings are just strings, they shouldn't run code!

If this is the case, what would be a more idiomatic way to approach this problem? The proposed answer to the question linked above is to use string formatting with either the % syntax or the format() function, but I don't think that would work well with the number of variables in my string (around 50).

Upvotes: 2

Views: 2166

Answers (4)

Mayli
Mayli

Reputation: 571

The task is not that hard, why don't you just make some coding for fun? And here is the function almost does what you want.

import re
def TempEval(template,**kwargs):
    mark = re.compile('\${(.*?)}')
    for key in kwargs:
        exec('%s=%s'%(key,kwargs[key]))
    for item in mark.findall(template):
        template=template.replace('${%s}'%item,str(eval(item)))
    return template


print TempEval('The width times one is ${width}',width=5)
#The width times one is 5
print TempEval('The width times two is ${width*2}',width=5)
#The width times two is 10

Upvotes: 2

Niklas B.
Niklas B.

Reputation: 95298

You probably need a better templating engine. Jinja2 supports this kind of stuff and a lot more. I don't think the standard library has anything equally powerful, but from what I figured, the library is pure Python, so you can integrate it into your application by just copying it along.

If Jinja doesn't fit you for some reason, have a look at the Python wiki, which has a section specifically for those kinds of libraries. Amongst them is the very lightweight Templite, which is only one class and seems to do exactly what you need.

Upvotes: 2

Fred Foo
Fred Foo

Reputation: 363547

The Pythonic solution to this problem is to forget about string formatting and pass a list of arguments to one of the subprocess functions, e.g.

# I have no idea about convert's command line usage,
# so here's an example using echo.
subprocess.call(["echo", str(1 + 1), "bla"])

That way, there's no need to build a single string and no need to worry about quoting.

Upvotes: 2

Hugh Bothwell
Hugh Bothwell

Reputation: 56634

Why not use built-in string formatting?

width = 45
"Width times one is {width}".format(width=width)
"Width times two is {width}".format(width=2*width)

results in

Width times one is 45
Width times two is 90

Upvotes: 3

Related Questions