string interpolation with both local and module variables?

Alex Martelli aleaxit at yahoo.com
Wed May 2 07:50:06 EDT 2001


"Graham Guttocks" <graham_guttocks at yahoo.co.nz> wrote in message
news:mailman.988762323.7975.python-list at python.org...
> Greetings,
>
> I'm trying to do string interpolation on a textfile containing
> references to variables both in the local function, and also in one of
> my modules.
>
> As demonstrated below, this doesn't seem to work because variables in
> the calling function aren't available to the function doing the
> interpolation.  Any ideas?
>
>  def printmsg():
>      foo = 'FOO'
>      message = makemessage('/tmp/test.txt')
>
>  def makemessage(file):
>      fp = open(file)
>      template = fp.read()
>      from Defs import *
>      text = template % vars()
>      return text
>
> /tmp/test.txt contains:
>
> Hello %(USERNAME)s, how do you %(foo)s?

Just change the second line of printmsg() to:

       message = makemessage('/tmp/test.txt', vars())

and makemessage() itself to, for example:

   def makemessage(file, vardict):
       fp = open(file)
       template = fp.read()
       import Defs
       localdict = Defs.__dict__.copy()
       localdict.update(vardict)
       text = template % localdict
       return text

One _might_ try to workaround the need for the
caller to pass his vars(), but that would require
a lot of work and black magic -- just passing
it seems much simpler.

In makemessage, I have here assumed that variables
in the passed vardict have precedence over those
coming from module Defs; it's easy to make it the
other way around, of course, just switch two lines:
       localdict = vardict.copy()
       localdict.update(Defs.__dict__)


The general idea of having the caller pass the
dictionary in which variables are to be looked
up by preference [normally its vars()] seems
sound, but of course there are many alternative
implementations that may have advantages.  For
example, say that you want variable lookup for
each name XXX to occur this way:
    -- if present in the caller's vars(), use that
    -- else, if present in Defs, use that
    -- else, make believe the variable exists AND
        refers to a string of the form
            "***variable XXX undefined***"
       to allow a different style for debugging
       template-files rather than try/except in
       the sources.

OK, that's easy, too:

class ChainDicts:
    def __init__(self, *dicts):
        self.dicts = dicts
    def __getitem__(self, name):
        for dict in dicts:
            try: return dict[name]
            except KeyError: pass
        return "***var %s undefined***" % name

and then:

   def makemessage(file, vardict):
       import Defs
       dict = ChainDicts(vardict, Defs.__dict__)
       fp = open(file)
       template = fp.read()
       text = template % dict
       return text


Alex






More information about the Python-list mailing list