This class encapsulates a string with embedded variable names. They are usually evaluated when the object's __str__() method is called. You can specify that they be evaluated when the object is created by including the immediate argument to the constructor. The doc string at the top of the module has an example of its use.
Itamar Shtull-Trauring's printexpr.py inspired me to try this out.
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | """VarString.py
Define the VarString class that encapsulates a string with embedded
variables. For example:
from VarString import VarString
fromAddr = '"Samuel Clemens" <sam@marktwain.com>'
msg = VarString('''From: $fromAddr
To: $toAddr
Subject: A short message
Dear $toName,
Just a quick message to let you know that reports of my death
are greatly exaggerated.
Regards,
Samuel Clemens.
''')
# infoSource() returns a list of 2-tuples.
for toAddr, toName in infoSource():
sendMsg(str(msg))
"""
import types, re, sys
class VarString:
"""A string with embedded variables signified by $a $b etc."""
def __init__(self, string, immediate=None):
if type(string) != types.StringType:
raise TypeError
self.__immediate = immediate
if immediate:
self.__str = self.__process(string)
else:
self.__str = string
def __str__(self):
if self.__immediate:
return self.__str
else:
return self.__process(self.__str)
def __process(self, str):
r = re.compile("\$([A-Za-z_][A-Za-z0-9_]*)")
self.__caller_globals, self.__caller_locals = _caller_symbols()
newstr = r.sub(self.__embsub, str)
return newstr
def __embsub(self, match):
name = match.group(1)
if self.__caller_locals.has_key(name):
return str(self.__caller_locals[name])
elif self.__caller_globals.has_key(name):
return str(self.__caller_globals[name])
else:
raise NameError, "There is no variable named '%s'" % (name)
def _caller_symbols():
"""Global and local symbols from three calling frames back
Thanks to Itamar Shtull-Trauring for showing how this is done."""
try:
raise StandardError
except StandardError:
fr = sys.exc_info()[2].tb_frame
# We go back three calling frames: to __process() to
# __init__() or __str__() to its caller.
return (fr.f_back.f_back.f_back.f_globals,
fr.f_back.f_back.f_back.f_locals)
|
There are two improvements I would like to make:
Allow \$ to be used to embed dollar signs in the string. My understanding of regexps is not currently up to that, but I guess it is simple enough. (Actually I was a bit confused by only needing one backslash in the string that defines the regexp.)
When raising the NameError exception, include a traceback object to show the error as occurring in the frame that calls the VarString object's method. I couldn't figure out how to do that.
Should I catch an exception if str() fails on the object specified by the variable?
% string operator. I think that % string operator enables you to do almost the same more efficiently.