##// END OF EJS Templates
store: simplify class hierarchy
store: simplify class hierarchy

File last commit:

r6783:6d824dc8 default
r6898:69aeaaaf default
Show More
templater.py
171 lines | 5.8 KiB | text/x-python | PythonLexer
Vadim Gelfer
add doc comments to template code.
r1909 # templater.py - template expansion for output
#
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Matt Mackall
templates: move filters to their own module...
r5976 import re, sys, os
Dirkjan Ochtman
give better error message on non-existent mapfile (issue813)
r6337 from mercurial import util
Vadim Gelfer
use safer string parser for template engine.
r1901
Vadim Gelfer
make parsestring work with strings that do not have quotes.
r1902 def parsestring(s, quoted=True):
Vadim Gelfer
add doc comments to template code.
r1909 '''parse a string using simple c-like syntax.
string must be in quotes if quoted is True.'''
Vadim Gelfer
make parsestring work with strings that do not have quotes.
r1902 if quoted:
Matt Mackall
templater: simplify parsestring
r3639 if len(s) < 2 or s[0] != s[-1]:
raise SyntaxError(_('unmatched quotes'))
return s[1:-1].decode('string_escape')
Matt Mackall
templater: use str.decode in parse_string
r3632
return s.decode('string_escape')
Vadim Gelfer
move hgweb template code out to templater
r1896
class templater(object):
Vadim Gelfer
add doc comments to template code.
r1909 '''template expansion engine.
template expansion works like this. a map file contains key=value
pairs. if value is quoted, it is treated as string. otherwise, it
is treated as name of template file.
templater is asked to expand a key in map. it looks up key, and
TK Soh
minor typo fix in templater's docstring
r4334 looks for strings like this: {foo}. it expands {foo} by looking up
Vadim Gelfer
add doc comments to template code.
r1909 foo in map, and substituting it. expansion is recursive: it stops
when there is no more {foo} to replace.
expansion also allows formatting and filtering.
format uses key to expand each item in list. syntax is
{key%format}.
filter uses function to transform value. syntax is
{key|filter1|filter2|...}.'''
Matt Mackall
template: fold template() into __call__, minor optimizations...
r3638 template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
Vadim Gelfer
fix template bug that made hgweb break....
r1964 def __init__(self, mapfile, filters={}, defaults={}, cache={}):
Vadim Gelfer
add doc comments to template code.
r1909 '''set up template engine.
mapfile is name of file to read map definitions from.
filters is dict of functions. each transforms a value into another.
defaults is dict of default map definitions.'''
Vadim Gelfer
improve template errors when something is wrong.
r1905 self.mapfile = mapfile or 'template'
Shun-ichi Goto
Duplicate cache when creating templater.
r1975 self.cache = cache.copy()
Vadim Gelfer
move hgweb template code out to templater
r1896 self.map = {}
Vadim Gelfer
improve template errors when something is wrong.
r1905 self.base = (mapfile and os.path.dirname(mapfile)) or ''
Vadim Gelfer
move hgweb template code out to templater
r1896 self.filters = filters
Vadim Gelfer
fix template bug that made hgweb break....
r1964 self.defaults = defaults
Vadim Gelfer
move hgweb template code out to templater
r1896
Vadim Gelfer
improve template errors when something is wrong.
r1905 if not mapfile:
return
Dirkjan Ochtman
give better error message on non-existent mapfile (issue813)
r6337 if not os.path.exists(mapfile):
raise util.Abort(_('style not found: %s') % mapfile)
Vadim Gelfer
use safer string parser for template engine.
r1901 i = 0
Vadim Gelfer
move hgweb template code out to templater
r1896 for l in file(mapfile):
Vadim Gelfer
make --style=compact look for map-cmdline.compact....
r1914 l = l.strip()
Vadim Gelfer
use safer string parser for template engine.
r1901 i += 1
Vadim Gelfer
make --style=compact look for map-cmdline.compact....
r1914 if not l or l[0] in '#;': continue
Vadim Gelfer
improve template errors when something is wrong.
r1905 m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l)
Vadim Gelfer
move hgweb template code out to templater
r1896 if m:
Vadim Gelfer
improve template errors when something is wrong.
r1905 key, val = m.groups()
if val[0] in "'\"":
try:
self.cache[key] = parsestring(val)
except SyntaxError, inst:
raise SyntaxError('%s:%s: %s' %
(mapfile, i, inst.args[0]))
else:
self.map[key] = os.path.join(self.base, val)
Vadim Gelfer
move hgweb template code out to templater
r1896 else:
Vadim Gelfer
improve template errors when something is wrong.
r1905 raise SyntaxError(_("%s:%s: parse error") % (mapfile, i))
Vadim Gelfer
move hgweb template code out to templater
r1896
Vadim Gelfer
many small changes to templater....
r1899 def __contains__(self, key):
Matt Mackall
templater: simplify cache and remove filter argument in __call__
r3637 return key in self.cache or key in self.map
Vadim Gelfer
many small changes to templater....
r1899
Dirkjan Ochtman
templater: make a template a string-only iterator
r6783 def _template(self, t):
'''Get the template for the given template name. Use a local cache.'''
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if not t in self.cache:
Vadim Gelfer
improve template errors when something is wrong.
r1905 try:
Matt Mackall
templater: simplify cache and remove filter argument in __call__
r3637 self.cache[t] = file(self.map[t]).read()
Vadim Gelfer
improve template errors when something is wrong.
r1905 except IOError, inst:
raise IOError(inst.args[0], _('template file %s: %s') %
(self.map[t], inst.args[1]))
Dirkjan Ochtman
templater: make a template a string-only iterator
r6783 return self.cache[t]
Vadim Gelfer
move hgweb template code out to templater
r1896
Dirkjan Ochtman
templater: make a template a string-only iterator
r6783 def _process(self, tmpl, map):
'''Render a template. Returns a generator.'''
Vadim Gelfer
move hgweb template code out to templater
r1896 while tmpl:
Vadim Gelfer
move repeated work out of inner loops.
r1900 m = self.template_re.search(tmpl)
Matt Mackall
template: fold template() into __call__, minor optimizations...
r3638 if not m:
yield tmpl
break
Vadim Gelfer
move hgweb template code out to templater
r1896
Matt Mackall
template: fold template() into __call__, minor optimizations...
r3638 start, end = m.span(0)
key, format, fl = m.groups()
Matt Mackall
templater: simplify template function
r3636
Matt Mackall
template: fold template() into __call__, minor optimizations...
r3638 if start:
yield tmpl[:start]
tmpl = tmpl[end:]
Matt Mackall
templater: simplify template function
r3636
Matt Mackall
template: fold template() into __call__, minor optimizations...
r3638 if key in map:
v = map[key]
else:
v = self.defaults.get(key, "")
if callable(v):
v = v(**map)
if format:
if not hasattr(v, '__iter__'):
Matt Mackall
hgweb: minor improvements for new web style...
r6434 raise SyntaxError(_("Error expanding '%s%%%s'")
Matt Mackall
template: fold template() into __call__, minor optimizations...
r3638 % (key, format))
lm = map.copy()
for i in v:
lm.update(i)
Dirkjan Ochtman
templater: make a template a string-only iterator
r6783 t = self._template(format)
yield self._process(t, lm)
Matt Mackall
template: fold template() into __call__, minor optimizations...
r3638 else:
if fl:
Vadim Gelfer
move hgweb template code out to templater
r1896 for f in fl.split("|")[1:]:
Matt Mackall
templater: simplify cache and remove filter argument in __call__
r3637 v = self.filters[f](v)
Vadim Gelfer
move hgweb template code out to templater
r1896 yield v
Vadim Gelfer
move repeated work out of inner loops.
r1900
Dirkjan Ochtman
templater: make a template a string-only iterator
r6783 def __call__(self, t, **map):
'''Perform expansion. t is name of map element to expand. map contains
added elements for use during expansion. Is a generator.'''
tmpl = self._template(t)
iters = [self._process(tmpl, map)]
while iters:
try:
item = iters[0].next()
except StopIteration:
iters.pop(0)
continue
if isinstance(item, str):
yield item
elif item is None:
yield ''
elif hasattr(item, '__iter__'):
iters.insert(0, iter(item))
else:
yield str(item)
Vadim Gelfer
many small changes to templater....
r1899 def templatepath(name=None):
Vadim Gelfer
add doc comments to template code.
r1909 '''return location of template file or directory (if no name).
returns None if not found.'''
Vadim Gelfer
move changeset_templater into templater module.
r2189
Lee Cantey
Windows py2exe version didn't handle names given to templatepath() correctly
r2001 # executable version (py2exe) doesn't support __file__
if hasattr(sys, 'frozen'):
module = sys.executable
else:
module = __file__
Vadim Gelfer
many small changes to templater....
r1899 for f in 'templates', '../templates':
fl = f.split('/')
if name: fl.append(name)
Lee Cantey
Windows py2exe version didn't handle names given to templatepath() correctly
r2001 p = os.path.join(os.path.dirname(module), *fl)
Vadim Gelfer
many small changes to templater....
r1899 if (name and os.path.exists(p)) or os.path.isdir(p):
Vadim Gelfer
move hgweb.templatepath into templater
r1897 return os.path.normpath(p)
Vadim Gelfer
move changeset_templater into templater module.
r2189
Matt Mackall
templates: move filters to their own module...
r5976 def stringify(thing):
'''turn nested template iterator into string.'''
if hasattr(thing, '__iter__'):
return "".join([stringify(t) for t in thing if t is not None])
return str(thing)