[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