##// END OF EJS Templates
templater: add if/ifeq conditionals
Matt Mackall -
r17636:ce18dbcd default
parent child Browse files
Show More
@@ -1,443 +1,470 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import sys, os, re
9 import sys, os, re
10 import util, config, templatefilters, parser, error
10 import util, config, templatefilters, parser, error
11
11
12 # template parsing
12 # template parsing
13
13
14 elements = {
14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 ",": (2, None, ("list", 2)),
16 ",": (2, None, ("list", 2)),
17 "|": (5, None, ("|", 5)),
17 "|": (5, None, ("|", 5)),
18 "%": (6, None, ("%", 6)),
18 "%": (6, None, ("%", 6)),
19 ")": (0, None, None),
19 ")": (0, None, None),
20 "symbol": (0, ("symbol",), None),
20 "symbol": (0, ("symbol",), None),
21 "string": (0, ("string",), None),
21 "string": (0, ("string",), None),
22 "end": (0, None, None),
22 "end": (0, None, None),
23 }
23 }
24
24
25 def tokenizer(data):
25 def tokenizer(data):
26 program, start, end = data
26 program, start, end = data
27 pos = start
27 pos = start
28 while pos < end:
28 while pos < end:
29 c = program[pos]
29 c = program[pos]
30 if c.isspace(): # skip inter-token whitespace
30 if c.isspace(): # skip inter-token whitespace
31 pass
31 pass
32 elif c in "(,)%|": # handle simple operators
32 elif c in "(,)%|": # handle simple operators
33 yield (c, None, pos)
33 yield (c, None, pos)
34 elif (c in '"\'' or c == 'r' and
34 elif (c in '"\'' or c == 'r' and
35 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
35 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
36 if c == 'r':
36 if c == 'r':
37 pos += 1
37 pos += 1
38 c = program[pos]
38 c = program[pos]
39 decode = False
39 decode = False
40 else:
40 else:
41 decode = True
41 decode = True
42 pos += 1
42 pos += 1
43 s = pos
43 s = pos
44 while pos < end: # find closing quote
44 while pos < end: # find closing quote
45 d = program[pos]
45 d = program[pos]
46 if decode and d == '\\': # skip over escaped characters
46 if decode and d == '\\': # skip over escaped characters
47 pos += 2
47 pos += 2
48 continue
48 continue
49 if d == c:
49 if d == c:
50 if not decode:
50 if not decode:
51 yield ('string', program[s:pos].replace('\\', r'\\'), s)
51 yield ('string', program[s:pos].replace('\\', r'\\'), s)
52 break
52 break
53 yield ('string', program[s:pos].decode('string-escape'), s)
53 yield ('string', program[s:pos].decode('string-escape'), s)
54 break
54 break
55 pos += 1
55 pos += 1
56 else:
56 else:
57 raise error.ParseError(_("unterminated string"), s)
57 raise error.ParseError(_("unterminated string"), s)
58 elif c.isalnum() or c in '_':
58 elif c.isalnum() or c in '_':
59 s = pos
59 s = pos
60 pos += 1
60 pos += 1
61 while pos < end: # find end of symbol
61 while pos < end: # find end of symbol
62 d = program[pos]
62 d = program[pos]
63 if not (d.isalnum() or d == "_"):
63 if not (d.isalnum() or d == "_"):
64 break
64 break
65 pos += 1
65 pos += 1
66 sym = program[s:pos]
66 sym = program[s:pos]
67 yield ('symbol', sym, s)
67 yield ('symbol', sym, s)
68 pos -= 1
68 pos -= 1
69 elif c == '}':
69 elif c == '}':
70 pos += 1
70 pos += 1
71 break
71 break
72 else:
72 else:
73 raise error.ParseError(_("syntax error"), pos)
73 raise error.ParseError(_("syntax error"), pos)
74 pos += 1
74 pos += 1
75 yield ('end', None, pos)
75 yield ('end', None, pos)
76
76
77 def compiletemplate(tmpl, context):
77 def compiletemplate(tmpl, context):
78 parsed = []
78 parsed = []
79 pos, stop = 0, len(tmpl)
79 pos, stop = 0, len(tmpl)
80 p = parser.parser(tokenizer, elements)
80 p = parser.parser(tokenizer, elements)
81
81
82 while pos < stop:
82 while pos < stop:
83 n = tmpl.find('{', pos)
83 n = tmpl.find('{', pos)
84 if n < 0:
84 if n < 0:
85 parsed.append(("string", tmpl[pos:]))
85 parsed.append(("string", tmpl[pos:]))
86 break
86 break
87 if n > 0 and tmpl[n - 1] == '\\':
87 if n > 0 and tmpl[n - 1] == '\\':
88 # escaped
88 # escaped
89 parsed.append(("string", tmpl[pos:n - 1] + "{"))
89 parsed.append(("string", tmpl[pos:n - 1] + "{"))
90 pos = n + 1
90 pos = n + 1
91 continue
91 continue
92 if n > pos:
92 if n > pos:
93 parsed.append(("string", tmpl[pos:n]))
93 parsed.append(("string", tmpl[pos:n]))
94
94
95 pd = [tmpl, n + 1, stop]
95 pd = [tmpl, n + 1, stop]
96 parseres, pos = p.parse(pd)
96 parseres, pos = p.parse(pd)
97 parsed.append(parseres)
97 parsed.append(parseres)
98
98
99 return [compileexp(e, context) for e in parsed]
99 return [compileexp(e, context) for e in parsed]
100
100
101 def compileexp(exp, context):
101 def compileexp(exp, context):
102 t = exp[0]
102 t = exp[0]
103 if t in methods:
103 if t in methods:
104 return methods[t](exp, context)
104 return methods[t](exp, context)
105 raise error.ParseError(_("unknown method '%s'") % t)
105 raise error.ParseError(_("unknown method '%s'") % t)
106
106
107 # template evaluation
107 # template evaluation
108
108
109 def getsymbol(exp):
109 def getsymbol(exp):
110 if exp[0] == 'symbol':
110 if exp[0] == 'symbol':
111 return exp[1]
111 return exp[1]
112 raise error.ParseError(_("expected a symbol"))
112 raise error.ParseError(_("expected a symbol"))
113
113
114 def getlist(x):
114 def getlist(x):
115 if not x:
115 if not x:
116 return []
116 return []
117 if x[0] == 'list':
117 if x[0] == 'list':
118 return getlist(x[1]) + [x[2]]
118 return getlist(x[1]) + [x[2]]
119 return [x]
119 return [x]
120
120
121 def getfilter(exp, context):
121 def getfilter(exp, context):
122 f = getsymbol(exp)
122 f = getsymbol(exp)
123 if f not in context._filters:
123 if f not in context._filters:
124 raise error.ParseError(_("unknown function '%s'") % f)
124 raise error.ParseError(_("unknown function '%s'") % f)
125 return context._filters[f]
125 return context._filters[f]
126
126
127 def gettemplate(exp, context):
127 def gettemplate(exp, context):
128 if exp[0] == 'string':
128 if exp[0] == 'string':
129 return compiletemplate(exp[1], context)
129 return compiletemplate(exp[1], context)
130 if exp[0] == 'symbol':
130 if exp[0] == 'symbol':
131 return context._load(exp[1])
131 return context._load(exp[1])
132 raise error.ParseError(_("expected template specifier"))
132 raise error.ParseError(_("expected template specifier"))
133
133
134 def runstring(context, mapping, data):
134 def runstring(context, mapping, data):
135 return data
135 return data
136
136
137 def runsymbol(context, mapping, key):
137 def runsymbol(context, mapping, key):
138 v = mapping.get(key)
138 v = mapping.get(key)
139 if v is None:
139 if v is None:
140 v = context._defaults.get(key, '')
140 v = context._defaults.get(key, '')
141 if util.safehasattr(v, '__call__'):
141 if util.safehasattr(v, '__call__'):
142 return v(**mapping)
142 return v(**mapping)
143 return v
143 return v
144
144
145 def buildfilter(exp, context):
145 def buildfilter(exp, context):
146 func, data = compileexp(exp[1], context)
146 func, data = compileexp(exp[1], context)
147 filt = getfilter(exp[2], context)
147 filt = getfilter(exp[2], context)
148 return (runfilter, (func, data, filt))
148 return (runfilter, (func, data, filt))
149
149
150 def runfilter(context, mapping, data):
150 def runfilter(context, mapping, data):
151 func, data, filt = data
151 func, data, filt = data
152 try:
152 try:
153 return filt(func(context, mapping, data))
153 return filt(func(context, mapping, data))
154 except (ValueError, AttributeError, TypeError):
154 except (ValueError, AttributeError, TypeError):
155 if isinstance(data, tuple):
155 if isinstance(data, tuple):
156 dt = data[1]
156 dt = data[1]
157 else:
157 else:
158 dt = data
158 dt = data
159 raise util.Abort(_("template filter '%s' is not compatible with "
159 raise util.Abort(_("template filter '%s' is not compatible with "
160 "keyword '%s'") % (filt.func_name, dt))
160 "keyword '%s'") % (filt.func_name, dt))
161
161
162 def buildmap(exp, context):
162 def buildmap(exp, context):
163 func, data = compileexp(exp[1], context)
163 func, data = compileexp(exp[1], context)
164 ctmpl = gettemplate(exp[2], context)
164 ctmpl = gettemplate(exp[2], context)
165 return (runmap, (func, data, ctmpl))
165 return (runmap, (func, data, ctmpl))
166
166
167 def runtemplate(context, mapping, template):
167 def runtemplate(context, mapping, template):
168 for func, data in template:
168 for func, data in template:
169 yield func(context, mapping, data)
169 yield func(context, mapping, data)
170
170
171 def runmap(context, mapping, data):
171 def runmap(context, mapping, data):
172 func, data, ctmpl = data
172 func, data, ctmpl = data
173 d = func(context, mapping, data)
173 d = func(context, mapping, data)
174 if util.safehasattr(d, '__call__'):
174 if util.safehasattr(d, '__call__'):
175 d = d()
175 d = d()
176
176
177 lm = mapping.copy()
177 lm = mapping.copy()
178
178
179 for i in d:
179 for i in d:
180 if isinstance(i, dict):
180 if isinstance(i, dict):
181 lm.update(i)
181 lm.update(i)
182 yield runtemplate(context, lm, ctmpl)
182 yield runtemplate(context, lm, ctmpl)
183 else:
183 else:
184 # v is not an iterable of dicts, this happen when 'key'
184 # v is not an iterable of dicts, this happen when 'key'
185 # has been fully expanded already and format is useless.
185 # has been fully expanded already and format is useless.
186 # If so, return the expanded value.
186 # If so, return the expanded value.
187 yield i
187 yield i
188
188
189 def buildfunc(exp, context):
189 def buildfunc(exp, context):
190 n = getsymbol(exp[1])
190 n = getsymbol(exp[1])
191 args = [compileexp(x, context) for x in getlist(exp[2])]
191 args = [compileexp(x, context) for x in getlist(exp[2])]
192 if n in funcs:
192 if n in funcs:
193 f = funcs[n]
193 f = funcs[n]
194 return (f, args)
194 return (f, args)
195 if n in context._filters:
195 if n in context._filters:
196 if len(args) != 1:
196 if len(args) != 1:
197 raise error.ParseError(_("filter %s expects one argument") % n)
197 raise error.ParseError(_("filter %s expects one argument") % n)
198 f = context._filters[n]
198 f = context._filters[n]
199 return (runfilter, (args[0][0], args[0][1], f))
199 return (runfilter, (args[0][0], args[0][1], f))
200
200
201 def join(context, mapping, args):
201 def join(context, mapping, args):
202 if not (1 <= len(args) <= 2):
202 if not (1 <= len(args) <= 2):
203 raise error.ParseError(_("join expects one or two arguments"))
203 raise error.ParseError(_("join expects one or two arguments"))
204
204
205 joinset = args[0][0](context, mapping, args[0][1])
205 joinset = args[0][0](context, mapping, args[0][1])
206 if util.safehasattr(joinset, '__call__'):
206 if util.safehasattr(joinset, '__call__'):
207 joinset = [x.values()[0] for x in joinset()]
207 joinset = [x.values()[0] for x in joinset()]
208
208
209 joiner = " "
209 joiner = " "
210 if len(args) > 1:
210 if len(args) > 1:
211 joiner = args[1][0](context, mapping, args[1][1])
211 joiner = args[1][0](context, mapping, args[1][1])
212
212
213 first = True
213 first = True
214 for x in joinset:
214 for x in joinset:
215 if first:
215 if first:
216 first = False
216 first = False
217 else:
217 else:
218 yield joiner
218 yield joiner
219 yield x
219 yield x
220
220
221 def sub(context, mapping, args):
221 def sub(context, mapping, args):
222 if len(args) != 3:
222 if len(args) != 3:
223 raise error.ParseError(_("sub expects three arguments"))
223 raise error.ParseError(_("sub expects three arguments"))
224
224
225 pat = stringify(args[0][0](context, mapping, args[0][1]))
225 pat = stringify(args[0][0](context, mapping, args[0][1]))
226 rpl = stringify(args[1][0](context, mapping, args[1][1]))
226 rpl = stringify(args[1][0](context, mapping, args[1][1]))
227 src = stringify(args[2][0](context, mapping, args[2][1]))
227 src = stringify(args[2][0](context, mapping, args[2][1]))
228 yield re.sub(pat, rpl, src)
228 yield re.sub(pat, rpl, src)
229
229
230 def if_(context, mapping, args):
231 if not (2 <= len(args) <= 3):
232 raise error.ParseError(_("if expects two or three arguments"))
233
234 test = stringify(args[0][0](context, mapping, args[0][1]))
235 if test:
236 t = stringify(args[1][0](context, mapping, args[1][1]))
237 yield runtemplate(context, mapping, compiletemplate(t, context))
238 elif len(args) == 3:
239 t = stringify(args[2][0](context, mapping, args[2][1]))
240 yield runtemplate(context, mapping, compiletemplate(t, context))
241
242 def ifeq(context, mapping, args):
243 if not (3 <= len(args) <= 4):
244 raise error.ParseError(_("ifeq expects three or four arguments"))
245
246 test = stringify(args[0][0](context, mapping, args[0][1]))
247 match = stringify(args[1][0](context, mapping, args[1][1]))
248 if test == match:
249 t = stringify(args[2][0](context, mapping, args[2][1]))
250 yield runtemplate(context, mapping, compiletemplate(t, context))
251 elif len(args) == 4:
252 t = stringify(args[3][0](context, mapping, args[3][1]))
253 yield runtemplate(context, mapping, compiletemplate(t, context))
254
230 methods = {
255 methods = {
231 "string": lambda e, c: (runstring, e[1]),
256 "string": lambda e, c: (runstring, e[1]),
232 "symbol": lambda e, c: (runsymbol, e[1]),
257 "symbol": lambda e, c: (runsymbol, e[1]),
233 "group": lambda e, c: compileexp(e[1], c),
258 "group": lambda e, c: compileexp(e[1], c),
234 # ".": buildmember,
259 # ".": buildmember,
235 "|": buildfilter,
260 "|": buildfilter,
236 "%": buildmap,
261 "%": buildmap,
237 "func": buildfunc,
262 "func": buildfunc,
238 }
263 }
239
264
240 funcs = {
265 funcs = {
266 "if": if_,
267 "ifeq": ifeq,
241 "join": join,
268 "join": join,
242 "sub": sub,
269 "sub": sub,
243 }
270 }
244
271
245 # template engine
272 # template engine
246
273
247 path = ['templates', '../templates']
274 path = ['templates', '../templates']
248 stringify = templatefilters.stringify
275 stringify = templatefilters.stringify
249
276
250 def _flatten(thing):
277 def _flatten(thing):
251 '''yield a single stream from a possibly nested set of iterators'''
278 '''yield a single stream from a possibly nested set of iterators'''
252 if isinstance(thing, str):
279 if isinstance(thing, str):
253 yield thing
280 yield thing
254 elif not util.safehasattr(thing, '__iter__'):
281 elif not util.safehasattr(thing, '__iter__'):
255 if thing is not None:
282 if thing is not None:
256 yield str(thing)
283 yield str(thing)
257 else:
284 else:
258 for i in thing:
285 for i in thing:
259 if isinstance(i, str):
286 if isinstance(i, str):
260 yield i
287 yield i
261 elif not util.safehasattr(i, '__iter__'):
288 elif not util.safehasattr(i, '__iter__'):
262 if i is not None:
289 if i is not None:
263 yield str(i)
290 yield str(i)
264 elif i is not None:
291 elif i is not None:
265 for j in _flatten(i):
292 for j in _flatten(i):
266 yield j
293 yield j
267
294
268 def parsestring(s, quoted=True):
295 def parsestring(s, quoted=True):
269 '''parse a string using simple c-like syntax.
296 '''parse a string using simple c-like syntax.
270 string must be in quotes if quoted is True.'''
297 string must be in quotes if quoted is True.'''
271 if quoted:
298 if quoted:
272 if len(s) < 2 or s[0] != s[-1]:
299 if len(s) < 2 or s[0] != s[-1]:
273 raise SyntaxError(_('unmatched quotes'))
300 raise SyntaxError(_('unmatched quotes'))
274 return s[1:-1].decode('string_escape')
301 return s[1:-1].decode('string_escape')
275
302
276 return s.decode('string_escape')
303 return s.decode('string_escape')
277
304
278 class engine(object):
305 class engine(object):
279 '''template expansion engine.
306 '''template expansion engine.
280
307
281 template expansion works like this. a map file contains key=value
308 template expansion works like this. a map file contains key=value
282 pairs. if value is quoted, it is treated as string. otherwise, it
309 pairs. if value is quoted, it is treated as string. otherwise, it
283 is treated as name of template file.
310 is treated as name of template file.
284
311
285 templater is asked to expand a key in map. it looks up key, and
312 templater is asked to expand a key in map. it looks up key, and
286 looks for strings like this: {foo}. it expands {foo} by looking up
313 looks for strings like this: {foo}. it expands {foo} by looking up
287 foo in map, and substituting it. expansion is recursive: it stops
314 foo in map, and substituting it. expansion is recursive: it stops
288 when there is no more {foo} to replace.
315 when there is no more {foo} to replace.
289
316
290 expansion also allows formatting and filtering.
317 expansion also allows formatting and filtering.
291
318
292 format uses key to expand each item in list. syntax is
319 format uses key to expand each item in list. syntax is
293 {key%format}.
320 {key%format}.
294
321
295 filter uses function to transform value. syntax is
322 filter uses function to transform value. syntax is
296 {key|filter1|filter2|...}.'''
323 {key|filter1|filter2|...}.'''
297
324
298 def __init__(self, loader, filters={}, defaults={}):
325 def __init__(self, loader, filters={}, defaults={}):
299 self._loader = loader
326 self._loader = loader
300 self._filters = filters
327 self._filters = filters
301 self._defaults = defaults
328 self._defaults = defaults
302 self._cache = {}
329 self._cache = {}
303
330
304 def _load(self, t):
331 def _load(self, t):
305 '''load, parse, and cache a template'''
332 '''load, parse, and cache a template'''
306 if t not in self._cache:
333 if t not in self._cache:
307 self._cache[t] = compiletemplate(self._loader(t), self)
334 self._cache[t] = compiletemplate(self._loader(t), self)
308 return self._cache[t]
335 return self._cache[t]
309
336
310 def process(self, t, mapping):
337 def process(self, t, mapping):
311 '''Perform expansion. t is name of map element to expand.
338 '''Perform expansion. t is name of map element to expand.
312 mapping contains added elements for use during expansion. Is a
339 mapping contains added elements for use during expansion. Is a
313 generator.'''
340 generator.'''
314 return _flatten(func(self, mapping, data) for func, data in
341 return _flatten(func(self, mapping, data) for func, data in
315 self._load(t))
342 self._load(t))
316 return _flatten(runtemplate(self, mapping, self._load(t)))
343 return _flatten(runtemplate(self, mapping, self._load(t)))
317
344
318 engines = {'default': engine}
345 engines = {'default': engine}
319
346
320 class templater(object):
347 class templater(object):
321
348
322 def __init__(self, mapfile, filters={}, defaults={}, cache={},
349 def __init__(self, mapfile, filters={}, defaults={}, cache={},
323 minchunk=1024, maxchunk=65536):
350 minchunk=1024, maxchunk=65536):
324 '''set up template engine.
351 '''set up template engine.
325 mapfile is name of file to read map definitions from.
352 mapfile is name of file to read map definitions from.
326 filters is dict of functions. each transforms a value into another.
353 filters is dict of functions. each transforms a value into another.
327 defaults is dict of default map definitions.'''
354 defaults is dict of default map definitions.'''
328 self.mapfile = mapfile or 'template'
355 self.mapfile = mapfile or 'template'
329 self.cache = cache.copy()
356 self.cache = cache.copy()
330 self.map = {}
357 self.map = {}
331 self.base = (mapfile and os.path.dirname(mapfile)) or ''
358 self.base = (mapfile and os.path.dirname(mapfile)) or ''
332 self.filters = templatefilters.filters.copy()
359 self.filters = templatefilters.filters.copy()
333 self.filters.update(filters)
360 self.filters.update(filters)
334 self.defaults = defaults
361 self.defaults = defaults
335 self.minchunk, self.maxchunk = minchunk, maxchunk
362 self.minchunk, self.maxchunk = minchunk, maxchunk
336 self.ecache = {}
363 self.ecache = {}
337
364
338 if not mapfile:
365 if not mapfile:
339 return
366 return
340 if not os.path.exists(mapfile):
367 if not os.path.exists(mapfile):
341 raise util.Abort(_('style not found: %s') % mapfile)
368 raise util.Abort(_('style not found: %s') % mapfile)
342
369
343 conf = config.config()
370 conf = config.config()
344 conf.read(mapfile)
371 conf.read(mapfile)
345
372
346 for key, val in conf[''].items():
373 for key, val in conf[''].items():
347 if not val:
374 if not val:
348 raise SyntaxError(_('%s: missing value') % conf.source('', key))
375 raise SyntaxError(_('%s: missing value') % conf.source('', key))
349 if val[0] in "'\"":
376 if val[0] in "'\"":
350 try:
377 try:
351 self.cache[key] = parsestring(val)
378 self.cache[key] = parsestring(val)
352 except SyntaxError, inst:
379 except SyntaxError, inst:
353 raise SyntaxError('%s: %s' %
380 raise SyntaxError('%s: %s' %
354 (conf.source('', key), inst.args[0]))
381 (conf.source('', key), inst.args[0]))
355 else:
382 else:
356 val = 'default', val
383 val = 'default', val
357 if ':' in val[1]:
384 if ':' in val[1]:
358 val = val[1].split(':', 1)
385 val = val[1].split(':', 1)
359 self.map[key] = val[0], os.path.join(self.base, val[1])
386 self.map[key] = val[0], os.path.join(self.base, val[1])
360
387
361 def __contains__(self, key):
388 def __contains__(self, key):
362 return key in self.cache or key in self.map
389 return key in self.cache or key in self.map
363
390
364 def load(self, t):
391 def load(self, t):
365 '''Get the template for the given template name. Use a local cache.'''
392 '''Get the template for the given template name. Use a local cache.'''
366 if t not in self.cache:
393 if t not in self.cache:
367 try:
394 try:
368 self.cache[t] = util.readfile(self.map[t][1])
395 self.cache[t] = util.readfile(self.map[t][1])
369 except KeyError, inst:
396 except KeyError, inst:
370 raise util.Abort(_('"%s" not in template map') % inst.args[0])
397 raise util.Abort(_('"%s" not in template map') % inst.args[0])
371 except IOError, inst:
398 except IOError, inst:
372 raise IOError(inst.args[0], _('template file %s: %s') %
399 raise IOError(inst.args[0], _('template file %s: %s') %
373 (self.map[t][1], inst.args[1]))
400 (self.map[t][1], inst.args[1]))
374 return self.cache[t]
401 return self.cache[t]
375
402
376 def __call__(self, t, **mapping):
403 def __call__(self, t, **mapping):
377 ttype = t in self.map and self.map[t][0] or 'default'
404 ttype = t in self.map and self.map[t][0] or 'default'
378 if ttype not in self.ecache:
405 if ttype not in self.ecache:
379 self.ecache[ttype] = engines[ttype](self.load,
406 self.ecache[ttype] = engines[ttype](self.load,
380 self.filters, self.defaults)
407 self.filters, self.defaults)
381 proc = self.ecache[ttype]
408 proc = self.ecache[ttype]
382
409
383 stream = proc.process(t, mapping)
410 stream = proc.process(t, mapping)
384 if self.minchunk:
411 if self.minchunk:
385 stream = util.increasingchunks(stream, min=self.minchunk,
412 stream = util.increasingchunks(stream, min=self.minchunk,
386 max=self.maxchunk)
413 max=self.maxchunk)
387 return stream
414 return stream
388
415
389 def templatepath(name=None):
416 def templatepath(name=None):
390 '''return location of template file or directory (if no name).
417 '''return location of template file or directory (if no name).
391 returns None if not found.'''
418 returns None if not found.'''
392 normpaths = []
419 normpaths = []
393
420
394 # executable version (py2exe) doesn't support __file__
421 # executable version (py2exe) doesn't support __file__
395 if util.mainfrozen():
422 if util.mainfrozen():
396 module = sys.executable
423 module = sys.executable
397 else:
424 else:
398 module = __file__
425 module = __file__
399 for f in path:
426 for f in path:
400 if f.startswith('/'):
427 if f.startswith('/'):
401 p = f
428 p = f
402 else:
429 else:
403 fl = f.split('/')
430 fl = f.split('/')
404 p = os.path.join(os.path.dirname(module), *fl)
431 p = os.path.join(os.path.dirname(module), *fl)
405 if name:
432 if name:
406 p = os.path.join(p, name)
433 p = os.path.join(p, name)
407 if name and os.path.exists(p):
434 if name and os.path.exists(p):
408 return os.path.normpath(p)
435 return os.path.normpath(p)
409 elif os.path.isdir(p):
436 elif os.path.isdir(p):
410 normpaths.append(os.path.normpath(p))
437 normpaths.append(os.path.normpath(p))
411
438
412 return normpaths
439 return normpaths
413
440
414 def stylemap(styles, paths=None):
441 def stylemap(styles, paths=None):
415 """Return path to mapfile for a given style.
442 """Return path to mapfile for a given style.
416
443
417 Searches mapfile in the following locations:
444 Searches mapfile in the following locations:
418 1. templatepath/style/map
445 1. templatepath/style/map
419 2. templatepath/map-style
446 2. templatepath/map-style
420 3. templatepath/map
447 3. templatepath/map
421 """
448 """
422
449
423 if paths is None:
450 if paths is None:
424 paths = templatepath()
451 paths = templatepath()
425 elif isinstance(paths, str):
452 elif isinstance(paths, str):
426 paths = [paths]
453 paths = [paths]
427
454
428 if isinstance(styles, str):
455 if isinstance(styles, str):
429 styles = [styles]
456 styles = [styles]
430
457
431 for style in styles:
458 for style in styles:
432 if not style:
459 if not style:
433 continue
460 continue
434 locations = [os.path.join(style, 'map'), 'map-' + style]
461 locations = [os.path.join(style, 'map'), 'map-' + style]
435 locations.append('map')
462 locations.append('map')
436
463
437 for path in paths:
464 for path in paths:
438 for location in locations:
465 for location in locations:
439 mapfile = os.path.join(path, location)
466 mapfile = os.path.join(path, location)
440 if os.path.isfile(mapfile):
467 if os.path.isfile(mapfile):
441 return style, mapfile
468 return style, mapfile
442
469
443 raise RuntimeError("No hgweb templates found in %r" % paths)
470 raise RuntimeError("No hgweb templates found in %r" % paths)
General Comments 0
You need to be logged in to leave comments. Login now