"""Cobbler, the simplest Python templating system that could possibly work. http://www.geocities.com/baby-lemonade/safairy.html explains it all, really: At the foot of the bed, Otumba kept wogs for poisonous snacks such as the deadly cobbler and apply python. Features: * Dict-like interface; keys map to %(named)s format args. * Re-usable; modify any/all params and assemble() as needed. * str()-able, unicode() too * Encoding-aware; just set Template.encoding before load_template(). * Stray "%" chars auto-replaced with "%%". * Builtin subclasses for HTML Option, Checkbox, Hidden, and TableRows. * Custom subclasses are a snap! CherryPy example: import os.path localDir = os.path.dirname(__file__) import cobbler import myapp class Root: def default(self, city): main = cobbler.Template(localDir, 'templates', 'default.html') main[u'City'] = myapp.safe_html(city) main[u'ZipCodes'] = myapp.zips_for_city(city) return main default.exposed = True default.html: Zip Codes for %(City)s %(ZipCodes)s """ import codecs import os import re from xml.sax.saxutils import quoteattr as quote def warn(msg): # Override this if you want warnings to go to your log. import warnings warnings.warn(msg) class Template(object): """Interpolates Python values with a block of source text. The source text is usually written in a markup language like HTML. String-formatting is used to substitute Python values into the text. For example, the source text "Hello, %(audience)s!" might be interpolated with {'audience': 'World'} to produce "Hello, World!". """ params = {} encoding = "ISO-8859-1" templateText = u'' def __init__(self, *fileparts): self.exhausted = False self.params = self.__class__.params.copy() self.templateText = self.__class__.templateText if fileparts: self.templateText = self.load_template(*fileparts) def load_template(self, *fileparts): """Return the source text from a file.""" newFileName = os.path.join(*fileparts) infile = codecs.open(newFileName, "r", self.encoding) t = infile.read() infile.close() t = re.sub(r'\r\n', u'\n', t) # Replace single % with %% if not part of %(key)s clause t = re.sub(r'(? elements. Usage: 1. When the value is distinct from the display: opts = {} for eachKey, eachItem in aCollection.items(): opts[eachKey] = eachItem.value('Name') output = OptionElement().assemble_all(opts, selItem) 2. When the value is the same as the display: opts = ['value1', 'value2', 'value3'] output = OptionElement().assemble_all(opts, selItem) """ templateText = (u'\n') def assemble_all(self, optionList, matchValue, sortByDisplay=True): """Interpose supplied parameters into the predetermined template. If optionList is a sequence of strings, they will be interposed as the 'display' variables. If optionList is a sequence of tuples, they will be interposed as ('value', 'display'). If optionList is a dictionary of strings, it will be interposed as {'value': 'display'}. """ if optionList: if isinstance(optionList, dict): optionList = [(k, v) for k, v in optionList.iteritems()] if sortByDisplay: optionList.sort(lambda x, y: cmp(x[1], y[1])) elif isinstance(optionList, list): if isinstance(optionList[0], tuple): if sortByDisplay: optionList.sort(lambda x, y: cmp(x[1], y[1])) else: if sortByDisplay: optionList.sort() tParam = [] matchValue = "%s" % matchValue for eachValue in optionList: sel = "" # Since values may not be strings, coerce to string now in # order to test equality, .startswith in next instructions. if isinstance(eachValue, tuple): val = "%s" % eachValue[0] display = "%s" % eachValue[1] else: val = display = ("%s" % eachValue) if val == matchValue: sel = " selected='selected' " if display.startswith(" ") or display.endswith(" "): msg = ("Option element has leading or trailing spaces in " "display:\n'%s'.\nSome browsers strip such spaces " "on display and when submitting HTML forms." % val) warn(msg) tParam.append(self.assemble({u'isSelected': sel, u'value': val, u'display': display, })) return u"".join(tParam) class CheckboxElement(Template): """A Template for generating HTML elements Usage: output = cobbler.CheckboxElement().assemble_all(name, checked) """ def __init__(self, value=None): Template.__init__(self) if value is None: self.templateText = (u'') else: self.templateText = (u'') def assemble_all(self, name, checked): chk = u'' if checked: chk = u' checked="checked" ' return self.assemble({u'name': name, u'checked': chk}) class HiddenElement(Template): """A Template for generating HTML elements Usage: output = cobbler.HiddenElement().assemble_all(key) """ templateText = u'' def assemble_all(self, name, value): return self.assemble({u'name': quote(name), u'value': quote(value)}) class TableRows(Template): """Template for generating HTML elements.""" def __init__(self, trClass=None): self.exhausted = False self.trClass = trClass self.rows = [] def add_row(self, *args): self.rows.append(args) def assemble(self, final_rows=[]): if self.trClass is None: tr = u'' else: tr = u'' output = [] for row in (self.rows + final_rows): output.append(tr) for col in row: output.append(u" %s" % col) output.append("") return u'\n'.join(output)