Python Templates

by James Tauber. See blog entry to comment.

Originally posted to the Python Web SIG back in 2003. Published here May 2006 with minor changes.

Step 1

Simple string substitution can be achieved with the % operator:

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

Step 2

...which can be used with a dictionary:

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

Step 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" })

Step 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" })

Step 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 getattr(self, l[1])(self.dict[l[0]]) def upper (self, s): return s.upper() print Template({ "name" : "Guido" })

Step 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>

%(list|li)s

</ul>" % self def __getitem__ (self, key): l = key.split( "|" ) if len(l) == 1: return self.dict[key] else : return getattr(self, l[1])(self.dict[l[0]]) def li (self, l): return "

" .join([ "\t<li>%s</li>" % x for x in l]) print Template({ "list" : [ "foo" , "bar" , "baz" ]})

Step 7

The template can be moved into a class attribute:

class Template : _template = """<ul>

%(list|li)s

</ul>""" def __init__ (self, dict): self.dict = dict def __str__ (self): return self._template % self def __getitem__ (self, key): l = key.split( "|" ) if len(l) == 1: return self.dict[key] else : return getattr(self, l[1])(self.dict[l[0]]) def li (self, l): return "

" .join([ "\t<li>%s</li>" % x for x in l]) print Template({ "list" : [ "foo" , "bar" , "baz" ]})

Step 8

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

class Template : _template = """<ul>

%(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 getattr(self, arg)() else : raise KeyError(arg) else : func = l[1] return getattr(self, func)(self._process([arg])) def lst (self): return [ "foo" , "bar" , "baz" ] def li (self, l): return "

" .join([ "\t<li>%s</li>" % x for x in l]) print Template()

Step 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:

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 getattr(self, arg)() else : raise KeyError(arg) else : func = l[1] return getattr(self, func)(self._process([arg])) class LI_Template : _template = """\t<li>%s</li>""" 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 UL_Template (DictionaryTemplate): _template = """<ul>

%(lst|li)s

</ul>""" def li (self, input_list): return LI_Template(input_list) print UL_Template({ "lst" : [ "foo" , "bar" , "baz" ]})

Step 10

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

from step9 import DictionaryTemplate 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): _template = """\t<li>%s</li>""" class UL_Template (DictionaryTemplate): _template = """<ul>

%(lst|li)s

</ul>""" def li (self, input_list): return LI_Template(input_list) print UL_Template({ "lst" : [ "foo" , "bar" ]})

Step 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 getattr(self, arg)() else : raise KeyError(arg) else : func_name = l[1] if func_name in self.dict: func = self.dict[func_name] else : func = getattr(self, func_name) return func(self._process([arg])) from step10 import ListTemplate class LI_Template (ListTemplate): _template = """\t<li>%s</li>""" class UL_Template (DictionaryTemplate): _template = """<ul>

%(lst|li)s

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

Step 12

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