text processing: variable interpolation and more

Greg Weeks weeks at vitus.scs.agilent.com
Sun Sep 9 07:03:52 CEST 2001


Many programs that I've written professionally create text files in various
languages and formats.  Often, different invocations of the program create
similar files.  In practice, I have needed both variable interpolation and
conditional line inclusion in order to make these programs readable.  This
is easy in Python, and I'd like to share the method below.  (There is
nothing clever involved, and only 20 lines of code.)

Of course, variable interpolation is already part of Python.  To
interpolate the variable foo in a big string, just include:

    '''+str(foo)+'''

This doesn't seem like much of a burden.  But, to my surprise, it was
pretty ugly in practice.  I needed something simpler, and I settled on
inserting <foo> instead.  (And if I want to escape interpolation, <\foo>
does the trick.)

For conditional line inclusion, I settled on ending the line with |?
followed by a conditional expression.

For example, here is an example function that creates a little business
form letter:


##############################################################################
def print_form_letter(recipient, they_want_us):
    render(sys.stdout, '''\
Dear <recipient>:

We are pleased to hear that you have chosen BigCorp.|?        they_want_us
We are sorry to hear that you will not be using BigCorp.|?    not they_want_us

Our company is the best in its class, blah, blah, blah.

Sincerely yours,
Brad Majors, BigCorp Marketing
''', globals(), locals())
##############################################################################


render() is a simple function, included below.  Note that in order to
evaluate the expressions

    recipient
    they_want_us
    not they_want_us

render() must be passed the scope in which the string to be rendered
appears.  Unfortunately, as a result of the nested scopes recently added to
Python, THIS WILL NO LONGER BE POSSIBLE IN GENERAL.  Grrrr.  Still, that
doesn't much matter in practice, and I still find render() to be quite
useful.  Here it is:


##############################################################################
from string  import splitfields
from re      import search, sub


def render(f, string, globals, locals={}):
    assert string and string[-1] == "\n", "string must end in newline"

    def repl(mo, globals=globals, locals=locals):
	if mo.group(1):			# VARIABLE WAS ESCAPED
	    return  "<%s%s>" % (mo.group(1)[1:], mo.group(2))
	else:				# THE USUAL CASE
	    return  str(eval(mo.group(2), globals, locals))

    for line in splitfields(string[:-1], "\n"):
	mo = search(r"(.*)\|\?(.*)", line)
	if mo:
	    line = mo.group(1)
	    keep = eval(mo.group(2), globals, locals)
	else:
	    keep = 1
	if keep:
	    line = sub(r"<(\\*)(\w+)>", repl, line)
	    f.write(line)
	    f.write("\n")
##############################################################################


Regards,
Greg


PS: I store render() in a module named "markup".


PS: I had to write my own command-line parser too.  Those are the only
general-purpose utilities I've found necessary so far.  I love Python (but
not nested scopes :-)




More information about the Python-list mailing list