[Web-SIG] htmlgen

James Tauber jtauber at jtauber.com
Mon Nov 10 13:22:35 EST 2003


Here is something I wrote up a little while ago which builds up an
approach to templating based on %(var)s



## 1. Simple string substitution can be achieved with the % operator:

name = "Guido"
print "Hello %s!" % name


## 2. ...which can be used with a dictionary:

dict = {"name": "Guido"}                                              ###
print "Hello %(name)s!" % dict                                        ###


## 3. The template can be a class, where the dictionary is passed into
## the constructor and __str__ is overridden to make the substitution:

class Template:                                                       ###
    def __init__(self, dict):                                         ###
        self.dict = dict                                              ###
    def __str__(self):                                                ###
        return "Hello %(name)s!" % self.dict                          ### 

print Template({"name": "Guido"})


## 4. __getitem__ can be overridden to perform additional processing
## on values:

class Template:
    def __init__(self, dict):
        self.dict = dict
    def __str__(self):
        return "Hello %(name)s!" % self                               ### 
    def __getitem__(self, key):                                       ###
        return self.dict[key].upper()                                 ###

print Template({"name": "Guido"})


## 5. Processing can even be driven from the template itself, with
## %(...)s referencing a function to apply to the value:

class Template:
    def __init__(self, dict):
        self.dict = dict
    def __str__(self):
        return "Hello %(name)s. Hello %(name|upper)s!" % self         ###
    def __getitem__(self, key):
        l = key.split("|")                                            ###
        if len(l) == 1:                                               ###
            return self.dict[key]                                     ###
        else:                                                         ###
            return apply(getattr(self, l[1]), [self.dict[l[0]]])      ### 
    def upper(self, s):                                               ### 
        return s.upper()                                              ###

print Template({"name": "Guido"})


## 6. Values in the dictionary can even be lists whose items are
## processed individually:

class Template:
    def __init__(self, dict):
        self.dict = dict
    def __str__(self):
        return "<ul>\n%(list|li)s</ul>" % self                        ###
    def __getitem__(self, key):
        l = key.split("|")
        if len(l) == 1:
            return self.dict[key]
        else:
            return apply(getattr(self, l[1]), [self.dict[l[0]]])
    def li(self, l):                                                  ###
        return "".join(["  <li>%s</li>\n" % x for x in l])            ###

print Template({"list": ["foo", "bar", "baz"]})


## 7. The template can be moved into a class attribute:

class Template:
    __template = """<ul>\n%(list|li)s</ul>"""                         ###
    def __init__(self, dict):
        self.dict = dict
    def __str__(self):
        return Template.__template % self                             ###
    def __getitem__(self, key):
        l = key.split("|")
        if len(l) == 1:
            return self.dict[key]
        else:
            return apply(getattr(self, l[1]), [self.dict[l[0]]])
    def li(self, l):
        return "".join(["  <li>%s</li>\n" % x for x in l])

print Template({"list": ["foo", "bar", "baz"]})


## 8. In some cases, you may want a value to come from a method rather
## than the dictionary:

class Template:
    def __template(self):                                             ###
        return """<ul>\n%(lst|li)s</ul>"""                            ###
    def __init__(self, dict={}):
        self.dict = dict
    def __str__(self):
        return self.__template() % self
    def __getitem__(self, key):
        return self.__process(key.split("|"))                         ###
    def __process(self, l):                                           ###
        arg = l[0]                                                    ###
        if len(l) == 1:                                               ###
            if arg in self.dict:                                      ###
                return self.dict[arg]                                 ### 
            elif hasattr(self, arg) and callable(getattr(self, arg)): ###
                return apply(getattr(self, arg), [])                  ###
            else:                                                     ###
                raise "can't retrieve %s" % arg                       ###
        else:                                                         ###
            func = l[1]                                               ###
            return apply(getattr(self, func), [self.__process([arg])])###
    def lst(self):                                                    ###
        return ["foo", "bar", "baz"]                                  ### 
    def li(self, l):
        return "".join(["  <li>%s</li>\n" % x for x in l])

print Template()


## 9. Now let's define a base template class and try multiple
## instances where we delegate formatting of the items to a
## different template than the overall list itself:

# the base template taken from previous example
class DictionaryTemplate:
    def __init__(self, dict={}):
        self.dict = dict
    def __str__(self):
        return self._template() % self
    def __getitem__(self, key):
        return self.__process(key.split("|"))
    def __process(self, l):
        arg = l[0]
        if len(l) == 1:
            if arg in self.dict:
                return self.dict[arg]
            elif hasattr(self, arg) and callable(getattr(self, arg)):
                return apply(getattr(self, arg), [])
            else:
                raise "can't retrieve %s" % arg
        else:
            func = l[1]
            return apply(getattr(self, func), [self.__process([arg])])

# template for individual items       
class LI_Template:                                                    ###
    __template = """  <li>%s</li>\n"""                                ### 
    def __init__(self, input_list=[]):                                ###
        self.input_list = input_list                                  ###
    def __str__(self):                                                ### 
        return "".join(                                               ###
            [LI_Template.__template % x for x in self.input_list])    ###

# template for overall list
class UL_Template(DictionaryTemplate):                                ###
    def _template(self):                                              ###
        return """<ul>\n%(lst|li)s</ul>"""                            ###
    def lst(self):                                                    ###
        return ["foo", "bar", "baz"]                                  ### 
    def li(self, input_list):                                         ###
        return LI_Template(input_list)                                ###

print UL_Template()


## 10. Much of the LI_Template can be refactored into a base class
## that does for lists what DictionaryTemplate does for dictionaries:

# assume class DictionaryTemplate exactly as before

class ListTemplate:                                                   ###
    def __init__(self, input_list=[]):                                ###
        self.input_list = input_list                                  ###
    def __str__(self):                                                ###
        return "".join(                                               ###
            [self._template() % x for x in self.input_list])          ###
        
class LI_Template(ListTemplate):                                      ###
    def _template(self):                                              ### 
        return """  <li>%s</li>\n"""                                  ### 

class UL_Template(DictionaryTemplate):
    def _template(self):
        return """<ul>\n%(lst|li)s</ul>"""
    def li(self, input_list):
        return LI_Template(input_list)

print UL_Template({"lst": ["foo", "bar"]})


## 11. We can make at least two more improvements to
## DictionaryTemplate. One is to allow keyword args to the
## constructor. The other is to change __process to support
## references to functions that are passed in (rather than being
## defined as methods):

class DictionaryTemplate:
    def __init__(self, dict={}, **keywords):                          ###
        self.dict = dict                                             
        self.dict.update(keywords)                                    ### 
    def __str__(self):
        return self._template() % self
    def __getitem__(self, key):
            return self.__process(key.split("|"))
    def __process(self, l):
        arg = l[0]
        if len(l) == 1:
            if arg in self.dict:
                return self.dict[arg]
            elif hasattr(self, arg) and callable(getattr(self, arg)):
                return apply(getattr(self, arg), [])
            else:
                raise "can't retrieve %s" % arg
        else:
            func_name = l[1]                                          ###
            if func_name in self.dict:                                ###
                func = self.dict[func_name]                           ### 
            else:                                                     ### 
                func = getattr(self, func_name)                       ###
            return  apply(func, [self.__process([arg])])              ###

# assume ListTemplate as before

class LI_Template(ListTemplate):
    def _template(self):
        return """  <li>%s</li>\n"""

class UL_Template(DictionaryTemplate):
    def _template(self):
        return """<ul>\n%(lst|li)s</ul>"""

print UL_Template(lst=["foo", "bar", "baz", "biz"], li=LI_Template)


## 12. Here is an example which starts to show a slightly more
## involved template.

# a list with no wrapper elements
class NakedList_Template(DictionaryTemplate):
    def _template(self):
        return """%(lst|li)s"""

# a template for an article
class Article_Template(ListTemplate):
    def _template(self):
        return """
        <div>
          <h3>%(heading)s</h3>
          <p class="date">%(date)s</p>
          <p>%(abstract)s</p>
          <p><a href="%(link)s">Link</a></p>
        </div>
        """

# the actual data
articles = [
    {"heading": "Article 1",
     "date": "2003-02-10",
     "abstract": "This is the first article.",
     "link": "http://example.com/article/1"},
    {"heading": "Article 2",
     "date": "2003-02-13",
     "abstract": "This is the second article.",
     "link": "http://example.com/article/2"}]

print NakedList_Template(lst=articles, li=Article_Template)



James
-- 
  James Tauber
  http://jtauber.com/



More information about the Web-SIG mailing list