In the early days of Aquarium, before I started using Cheetah, I had a decent interpolation library that fit in a single function, but was still quite handy:
def evalstr(s, dollar="$"):
"""Evaluate a string with embedded python expressions.
This is a Python version of the function evalstr, which is used for
a = 5
evalStr("I have $a$ dogs.")
"I have 5 dogs."
The code between the $'s can be any valid Python expression. I'll
just basically do
str(eval(expression), yourGlobals, yourLocals)
Since eval is being used, the expression must return some value.
To escape $'s in s, just use $$ (even within expressions). To use
a different character than $, pass the desired character in the
second (optional) argument. If there are an odd number of $
symbols, a ValueError exception will be raised. Here are some
additional examples (assuming a = 5):
"My dog is $a$ years old." -> "My dog is 5 years old.",
"I have $$5.00" -> "I have $5.00",
"My $a$ year old dog has $$5.00" -> "My 5 year old dog has $5.00",
"My dog has $'$$%s.' % a$" -> "My dog has $5.",
"My dog has $'$$' + str(a) + '.'" -> ValueError exception,
"My dog is $a$.$" -> ValueError exception,
"$$ is a char." -> "$ is a char.",
"$a$ is a number." -> "5 is a number.",
"$a$" -> "5",
"$a" -> ValueError exception,
"I have a $$" -> "I have a $",
"" -> "",
"Spam" -> "Spam",
"$" -> ValueError exception,
"$ evalstr('I am #a#.', '#') $" -> "I am 5."
# By the way, I'm sorry if this stuff is hard to read. If you
# can come up with something more elegant, I'd love to see it,
# but this is the best that I could come up with.
inEv = 0 # Are we in eval mode (i.e. inside a single $)?
inDollar = 0 # We just got a dollar. We might be in an escape.
ret = "" # This is the string we're returning.
tok = "" # The token we're currently working with.
stack =  # All good languages are stack based ;)
i=j=0 # These are indexes into s.
# Grab the calling function's namespace. I can't find a nice
# way to do this other than manually generating an exception
# and surfing the traceback. As for performance, you can do
# this about 10000 times in a bit less than a second on my
# PIII 600 mhz system.
traceback = sys.exc_info()
currFrame = traceback.tb_frame
prevFrame = currFrame.f_back
prevGlobals = prevFrame.f_globals
prevLocals = prevFrame.f_locals
# If they forget to close the $, raise a ValueError exception.
if string.count(s, dollar) % 2:
raise ValueError, "unmatched dollar symbol"
sLen = len(s)
# Get next token.
j = i
tok = ""
elif s[j] == dollar:
tok = dollar
i = j+1
while j<sLen and s[j]!=dollar: j=j+1
tok = s[i:]
i = sLen
tok = s[i:j]
i = j
# Deal with receiving $'s (may call continue).
if tok == dollar:
if inDollar: inDollar = 0
inDollar = 1
# Deal with appending stuff to the stack or to ret.
inEv = not inEv
inDollar = 0
if inEv: stack = stack + [tok]
if len(stack) > 0:
ret = ret + \
ret = ret + tok
stack = 
# Deal with end of string.
if tok == "": break