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