comparison mercurial/templater.py @ 1909:37b9f80a5fbb

add doc comments to template code.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Mon, 27 Feb 2006 15:07:25 -0800
parents 9dec2479622d
children b288b4bb8448
comparison
equal deleted inserted replaced
1908:be71c04d62c0 1909:37b9f80a5fbb
1 # templater.py - template expansion for output
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
1 import re 8 import re
2 from demandload import demandload 9 from demandload import demandload
3 from i18n import gettext as _ 10 from i18n import gettext as _
4 demandload(globals(), "cStringIO cgi os time urllib util") 11 demandload(globals(), "cStringIO cgi os time urllib util")
5 12
10 'n': '\n', 17 'n': '\n',
11 'v': '\v', 18 'v': '\v',
12 } 19 }
13 20
14 def parsestring(s, quoted=True): 21 def parsestring(s, quoted=True):
22 '''parse a string using simple c-like syntax.
23 string must be in quotes if quoted is True.'''
15 fp = cStringIO.StringIO() 24 fp = cStringIO.StringIO()
16 if quoted: 25 if quoted:
17 first = s[0] 26 first = s[0]
18 if len(s) < 2: raise SyntaxError(_('string too short')) 27 if len(s) < 2: raise SyntaxError(_('string too short'))
19 if first not in "'\"": raise SyntaxError(_('invalid quote')) 28 if first not in "'\"": raise SyntaxError(_('invalid quote'))
29 else: fp.write(c) 38 else: fp.write(c)
30 if escape: raise SyntaxError(_('unterminated escape')) 39 if escape: raise SyntaxError(_('unterminated escape'))
31 return fp.getvalue() 40 return fp.getvalue()
32 41
33 class templater(object): 42 class templater(object):
43 '''template expansion engine.
44
45 template expansion works like this. a map file contains key=value
46 pairs. if value is quoted, it is treated as string. otherwise, it
47 is treated as name of template file.
48
49 templater is asked to expand a key in map. it looks up key, and
50 looks for atrings like this: {foo}. it expands {foo} by looking up
51 foo in map, and substituting it. expansion is recursive: it stops
52 when there is no more {foo} to replace.
53
54 expansion also allows formatting and filtering.
55
56 format uses key to expand each item in list. syntax is
57 {key%format}.
58
59 filter uses function to transform value. syntax is
60 {key|filter1|filter2|...}.'''
61
34 def __init__(self, mapfile, filters={}, defaults={}): 62 def __init__(self, mapfile, filters={}, defaults={}):
63 '''set up template engine.
64 mapfile is name of file to read map definitions from.
65 filters is dict of functions. each transforms a value into another.
66 defaults is dict of default map definitions.'''
35 self.mapfile = mapfile or 'template' 67 self.mapfile = mapfile or 'template'
36 self.cache = {} 68 self.cache = {}
37 self.map = {} 69 self.map = {}
38 self.base = (mapfile and os.path.dirname(mapfile)) or '' 70 self.base = (mapfile and os.path.dirname(mapfile)) or ''
39 self.filters = filters 71 self.filters = filters
62 94
63 def __contains__(self, key): 95 def __contains__(self, key):
64 return key in self.cache 96 return key in self.cache
65 97
66 def __call__(self, t, **map): 98 def __call__(self, t, **map):
99 '''perform expansion.
100 t is name of map element to expand.
101 map is added elements to use during expansion.'''
67 m = self.defaults.copy() 102 m = self.defaults.copy()
68 m.update(map) 103 m.update(map)
69 try: 104 try:
70 tmpl = self.cache[t] 105 tmpl = self.cache[t]
71 except KeyError: 106 except KeyError:
125 ("month", 3600 * 24 * 30), 160 ("month", 3600 * 24 * 30),
126 ("year", 3600 * 24 * 365)] 161 ("year", 3600 * 24 * 365)]
127 162
128 agescales.reverse() 163 agescales.reverse()
129 164
130 def age(x): 165 def age(date):
166 '''turn a (timestamp, tzoff) tuple into an age string.'''
167
131 def plural(t, c): 168 def plural(t, c):
132 if c == 1: 169 if c == 1:
133 return t 170 return t
134 return t + "s" 171 return t + "s"
135 def fmt(t, c): 172 def fmt(t, c):
136 return "%d %s" % (c, plural(t, c)) 173 return "%d %s" % (c, plural(t, c))
137 174
138 now = time.time() 175 now = time.time()
139 then = x[0] 176 then = date[0]
140 delta = max(1, int(now - then)) 177 delta = max(1, int(now - then))
141 178
142 for t, s in agescales: 179 for t, s in agescales:
143 n = delta / s 180 n = delta / s
144 if n >= 2 or s == 1: 181 if n >= 2 or s == 1:
145 return fmt(t, n) 182 return fmt(t, n)
146 183
147 def isodate(date): 184 def isodate(date):
185 '''turn a (timestamp, tzoff) tuple into an iso 8631 date.'''
148 return util.datestr(date, format='%Y-%m-%d %H:%M') 186 return util.datestr(date, format='%Y-%m-%d %H:%M')
149 187
150 def nl2br(text): 188 def nl2br(text):
189 '''replace raw newlines with xhtml line breaks.'''
151 return text.replace('\n', '<br/>\n') 190 return text.replace('\n', '<br/>\n')
152 191
153 def obfuscate(text): 192 def obfuscate(text):
154 return ''.join(['&#%d;' % ord(c) for c in text]) 193 return ''.join(['&#%d;' % ord(c) for c in text])
155 194
156 def domain(author): 195 def domain(author):
196 '''get domain of author, or empty string if none.'''
157 f = author.find('@') 197 f = author.find('@')
158 if f == -1: return '' 198 if f == -1: return ''
159 author = author[f+1:] 199 author = author[f+1:]
160 f = author.find('>') 200 f = author.find('>')
161 if f >= 0: author = author[:f] 201 if f >= 0: author = author[:f]
162 return author 202 return author
163 203
164 def person(author): 204 def person(author):
205 '''get name of author, or else username.'''
165 f = author.find('<') 206 f = author.find('<')
166 if f == -1: return util.shortuser(author) 207 if f == -1: return util.shortuser(author)
167 return author[:f].rstrip() 208 return author[:f].rstrip()
168 209
169 common_filters = { 210 common_filters = {
183 "urlescape": urllib.quote, 224 "urlescape": urllib.quote,
184 "user": util.shortuser, 225 "user": util.shortuser,
185 } 226 }
186 227
187 def templatepath(name=None): 228 def templatepath(name=None):
229 '''return location of template file or directory (if no name).
230 returns None if not found.'''
188 for f in 'templates', '../templates': 231 for f in 'templates', '../templates':
189 fl = f.split('/') 232 fl = f.split('/')
190 if name: fl.append(name) 233 if name: fl.append(name)
191 p = os.path.join(os.path.dirname(__file__), *fl) 234 p = os.path.join(os.path.dirname(__file__), *fl)
192 if (name and os.path.exists(p)) or os.path.isdir(p): 235 if (name and os.path.exists(p)) or os.path.isdir(p):