##// END OF EJS Templates
i18n: add "i18n" comment to error messages of template functions
FUJIWARA Katsunori -
r17890:ca6850b9 stable
parent child Browse files
Show More
@@ -1,471 +1,475 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 templatefilters.funcs:
195 if n in templatefilters.funcs:
196 f = templatefilters.funcs[n]
196 f = templatefilters.funcs[n]
197 return (f, args)
197 return (f, args)
198 if n in context._filters:
198 if n in context._filters:
199 if len(args) != 1:
199 if len(args) != 1:
200 raise error.ParseError(_("filter %s expects one argument") % n)
200 raise error.ParseError(_("filter %s expects one argument") % n)
201 f = context._filters[n]
201 f = context._filters[n]
202 return (runfilter, (args[0][0], args[0][1], f))
202 return (runfilter, (args[0][0], args[0][1], f))
203
203
204 def join(context, mapping, args):
204 def join(context, mapping, args):
205 if not (1 <= len(args) <= 2):
205 if not (1 <= len(args) <= 2):
206 # i18n: "join" is a keyword
206 raise error.ParseError(_("join expects one or two arguments"))
207 raise error.ParseError(_("join expects one or two arguments"))
207
208
208 joinset = args[0][0](context, mapping, args[0][1])
209 joinset = args[0][0](context, mapping, args[0][1])
209 if util.safehasattr(joinset, '__call__'):
210 if util.safehasattr(joinset, '__call__'):
210 joinset = [x.values()[0] for x in joinset()]
211 joinset = [x.values()[0] for x in joinset()]
211
212
212 joiner = " "
213 joiner = " "
213 if len(args) > 1:
214 if len(args) > 1:
214 joiner = args[1][0](context, mapping, args[1][1])
215 joiner = args[1][0](context, mapping, args[1][1])
215
216
216 first = True
217 first = True
217 for x in joinset:
218 for x in joinset:
218 if first:
219 if first:
219 first = False
220 first = False
220 else:
221 else:
221 yield joiner
222 yield joiner
222 yield x
223 yield x
223
224
224 def sub(context, mapping, args):
225 def sub(context, mapping, args):
225 if len(args) != 3:
226 if len(args) != 3:
227 # i18n: "sub" is a keyword
226 raise error.ParseError(_("sub expects three arguments"))
228 raise error.ParseError(_("sub expects three arguments"))
227
229
228 pat = stringify(args[0][0](context, mapping, args[0][1]))
230 pat = stringify(args[0][0](context, mapping, args[0][1]))
229 rpl = stringify(args[1][0](context, mapping, args[1][1]))
231 rpl = stringify(args[1][0](context, mapping, args[1][1]))
230 src = stringify(args[2][0](context, mapping, args[2][1]))
232 src = stringify(args[2][0](context, mapping, args[2][1]))
231 yield re.sub(pat, rpl, src)
233 yield re.sub(pat, rpl, src)
232
234
233 def if_(context, mapping, args):
235 def if_(context, mapping, args):
234 if not (2 <= len(args) <= 3):
236 if not (2 <= len(args) <= 3):
237 # i18n: "if" is a keyword
235 raise error.ParseError(_("if expects two or three arguments"))
238 raise error.ParseError(_("if expects two or three arguments"))
236
239
237 test = stringify(args[0][0](context, mapping, args[0][1]))
240 test = stringify(args[0][0](context, mapping, args[0][1]))
238 if test:
241 if test:
239 t = stringify(args[1][0](context, mapping, args[1][1]))
242 t = stringify(args[1][0](context, mapping, args[1][1]))
240 yield runtemplate(context, mapping, compiletemplate(t, context))
243 yield runtemplate(context, mapping, compiletemplate(t, context))
241 elif len(args) == 3:
244 elif len(args) == 3:
242 t = stringify(args[2][0](context, mapping, args[2][1]))
245 t = stringify(args[2][0](context, mapping, args[2][1]))
243 yield runtemplate(context, mapping, compiletemplate(t, context))
246 yield runtemplate(context, mapping, compiletemplate(t, context))
244
247
245 def ifeq(context, mapping, args):
248 def ifeq(context, mapping, args):
246 if not (3 <= len(args) <= 4):
249 if not (3 <= len(args) <= 4):
250 # i18n: "ifeq" is a keyword
247 raise error.ParseError(_("ifeq expects three or four arguments"))
251 raise error.ParseError(_("ifeq expects three or four arguments"))
248
252
249 test = stringify(args[0][0](context, mapping, args[0][1]))
253 test = stringify(args[0][0](context, mapping, args[0][1]))
250 match = stringify(args[1][0](context, mapping, args[1][1]))
254 match = stringify(args[1][0](context, mapping, args[1][1]))
251 if test == match:
255 if test == match:
252 t = stringify(args[2][0](context, mapping, args[2][1]))
256 t = stringify(args[2][0](context, mapping, args[2][1]))
253 yield runtemplate(context, mapping, compiletemplate(t, context))
257 yield runtemplate(context, mapping, compiletemplate(t, context))
254 elif len(args) == 4:
258 elif len(args) == 4:
255 t = stringify(args[3][0](context, mapping, args[3][1]))
259 t = stringify(args[3][0](context, mapping, args[3][1]))
256 yield runtemplate(context, mapping, compiletemplate(t, context))
260 yield runtemplate(context, mapping, compiletemplate(t, context))
257
261
258 methods = {
262 methods = {
259 "string": lambda e, c: (runstring, e[1]),
263 "string": lambda e, c: (runstring, e[1]),
260 "symbol": lambda e, c: (runsymbol, e[1]),
264 "symbol": lambda e, c: (runsymbol, e[1]),
261 "group": lambda e, c: compileexp(e[1], c),
265 "group": lambda e, c: compileexp(e[1], c),
262 # ".": buildmember,
266 # ".": buildmember,
263 "|": buildfilter,
267 "|": buildfilter,
264 "%": buildmap,
268 "%": buildmap,
265 "func": buildfunc,
269 "func": buildfunc,
266 }
270 }
267
271
268 funcs = {
272 funcs = {
269 "if": if_,
273 "if": if_,
270 "ifeq": ifeq,
274 "ifeq": ifeq,
271 "join": join,
275 "join": join,
272 "sub": sub,
276 "sub": sub,
273 }
277 }
274
278
275 # template engine
279 # template engine
276
280
277 path = ['templates', '../templates']
281 path = ['templates', '../templates']
278 stringify = templatefilters.stringify
282 stringify = templatefilters.stringify
279
283
280 def _flatten(thing):
284 def _flatten(thing):
281 '''yield a single stream from a possibly nested set of iterators'''
285 '''yield a single stream from a possibly nested set of iterators'''
282 if isinstance(thing, str):
286 if isinstance(thing, str):
283 yield thing
287 yield thing
284 elif not util.safehasattr(thing, '__iter__'):
288 elif not util.safehasattr(thing, '__iter__'):
285 if thing is not None:
289 if thing is not None:
286 yield str(thing)
290 yield str(thing)
287 else:
291 else:
288 for i in thing:
292 for i in thing:
289 if isinstance(i, str):
293 if isinstance(i, str):
290 yield i
294 yield i
291 elif not util.safehasattr(i, '__iter__'):
295 elif not util.safehasattr(i, '__iter__'):
292 if i is not None:
296 if i is not None:
293 yield str(i)
297 yield str(i)
294 elif i is not None:
298 elif i is not None:
295 for j in _flatten(i):
299 for j in _flatten(i):
296 yield j
300 yield j
297
301
298 def parsestring(s, quoted=True):
302 def parsestring(s, quoted=True):
299 '''parse a string using simple c-like syntax.
303 '''parse a string using simple c-like syntax.
300 string must be in quotes if quoted is True.'''
304 string must be in quotes if quoted is True.'''
301 if quoted:
305 if quoted:
302 if len(s) < 2 or s[0] != s[-1]:
306 if len(s) < 2 or s[0] != s[-1]:
303 raise SyntaxError(_('unmatched quotes'))
307 raise SyntaxError(_('unmatched quotes'))
304 return s[1:-1].decode('string_escape')
308 return s[1:-1].decode('string_escape')
305
309
306 return s.decode('string_escape')
310 return s.decode('string_escape')
307
311
308 class engine(object):
312 class engine(object):
309 '''template expansion engine.
313 '''template expansion engine.
310
314
311 template expansion works like this. a map file contains key=value
315 template expansion works like this. a map file contains key=value
312 pairs. if value is quoted, it is treated as string. otherwise, it
316 pairs. if value is quoted, it is treated as string. otherwise, it
313 is treated as name of template file.
317 is treated as name of template file.
314
318
315 templater is asked to expand a key in map. it looks up key, and
319 templater is asked to expand a key in map. it looks up key, and
316 looks for strings like this: {foo}. it expands {foo} by looking up
320 looks for strings like this: {foo}. it expands {foo} by looking up
317 foo in map, and substituting it. expansion is recursive: it stops
321 foo in map, and substituting it. expansion is recursive: it stops
318 when there is no more {foo} to replace.
322 when there is no more {foo} to replace.
319
323
320 expansion also allows formatting and filtering.
324 expansion also allows formatting and filtering.
321
325
322 format uses key to expand each item in list. syntax is
326 format uses key to expand each item in list. syntax is
323 {key%format}.
327 {key%format}.
324
328
325 filter uses function to transform value. syntax is
329 filter uses function to transform value. syntax is
326 {key|filter1|filter2|...}.'''
330 {key|filter1|filter2|...}.'''
327
331
328 def __init__(self, loader, filters={}, defaults={}):
332 def __init__(self, loader, filters={}, defaults={}):
329 self._loader = loader
333 self._loader = loader
330 self._filters = filters
334 self._filters = filters
331 self._defaults = defaults
335 self._defaults = defaults
332 self._cache = {}
336 self._cache = {}
333
337
334 def _load(self, t):
338 def _load(self, t):
335 '''load, parse, and cache a template'''
339 '''load, parse, and cache a template'''
336 if t not in self._cache:
340 if t not in self._cache:
337 self._cache[t] = compiletemplate(self._loader(t), self)
341 self._cache[t] = compiletemplate(self._loader(t), self)
338 return self._cache[t]
342 return self._cache[t]
339
343
340 def process(self, t, mapping):
344 def process(self, t, mapping):
341 '''Perform expansion. t is name of map element to expand.
345 '''Perform expansion. t is name of map element to expand.
342 mapping contains added elements for use during expansion. Is a
346 mapping contains added elements for use during expansion. Is a
343 generator.'''
347 generator.'''
344 return _flatten(runtemplate(self, mapping, self._load(t)))
348 return _flatten(runtemplate(self, mapping, self._load(t)))
345
349
346 engines = {'default': engine}
350 engines = {'default': engine}
347
351
348 class templater(object):
352 class templater(object):
349
353
350 def __init__(self, mapfile, filters={}, defaults={}, cache={},
354 def __init__(self, mapfile, filters={}, defaults={}, cache={},
351 minchunk=1024, maxchunk=65536):
355 minchunk=1024, maxchunk=65536):
352 '''set up template engine.
356 '''set up template engine.
353 mapfile is name of file to read map definitions from.
357 mapfile is name of file to read map definitions from.
354 filters is dict of functions. each transforms a value into another.
358 filters is dict of functions. each transforms a value into another.
355 defaults is dict of default map definitions.'''
359 defaults is dict of default map definitions.'''
356 self.mapfile = mapfile or 'template'
360 self.mapfile = mapfile or 'template'
357 self.cache = cache.copy()
361 self.cache = cache.copy()
358 self.map = {}
362 self.map = {}
359 self.base = (mapfile and os.path.dirname(mapfile)) or ''
363 self.base = (mapfile and os.path.dirname(mapfile)) or ''
360 self.filters = templatefilters.filters.copy()
364 self.filters = templatefilters.filters.copy()
361 self.filters.update(filters)
365 self.filters.update(filters)
362 self.defaults = defaults
366 self.defaults = defaults
363 self.minchunk, self.maxchunk = minchunk, maxchunk
367 self.minchunk, self.maxchunk = minchunk, maxchunk
364 self.ecache = {}
368 self.ecache = {}
365
369
366 if not mapfile:
370 if not mapfile:
367 return
371 return
368 if not os.path.exists(mapfile):
372 if not os.path.exists(mapfile):
369 raise util.Abort(_('style not found: %s') % mapfile)
373 raise util.Abort(_('style not found: %s') % mapfile)
370
374
371 conf = config.config()
375 conf = config.config()
372 conf.read(mapfile)
376 conf.read(mapfile)
373
377
374 for key, val in conf[''].items():
378 for key, val in conf[''].items():
375 if not val:
379 if not val:
376 raise SyntaxError(_('%s: missing value') % conf.source('', key))
380 raise SyntaxError(_('%s: missing value') % conf.source('', key))
377 if val[0] in "'\"":
381 if val[0] in "'\"":
378 try:
382 try:
379 self.cache[key] = parsestring(val)
383 self.cache[key] = parsestring(val)
380 except SyntaxError, inst:
384 except SyntaxError, inst:
381 raise SyntaxError('%s: %s' %
385 raise SyntaxError('%s: %s' %
382 (conf.source('', key), inst.args[0]))
386 (conf.source('', key), inst.args[0]))
383 else:
387 else:
384 val = 'default', val
388 val = 'default', val
385 if ':' in val[1]:
389 if ':' in val[1]:
386 val = val[1].split(':', 1)
390 val = val[1].split(':', 1)
387 self.map[key] = val[0], os.path.join(self.base, val[1])
391 self.map[key] = val[0], os.path.join(self.base, val[1])
388
392
389 def __contains__(self, key):
393 def __contains__(self, key):
390 return key in self.cache or key in self.map
394 return key in self.cache or key in self.map
391
395
392 def load(self, t):
396 def load(self, t):
393 '''Get the template for the given template name. Use a local cache.'''
397 '''Get the template for the given template name. Use a local cache.'''
394 if t not in self.cache:
398 if t not in self.cache:
395 try:
399 try:
396 self.cache[t] = util.readfile(self.map[t][1])
400 self.cache[t] = util.readfile(self.map[t][1])
397 except KeyError, inst:
401 except KeyError, inst:
398 raise util.Abort(_('"%s" not in template map') % inst.args[0])
402 raise util.Abort(_('"%s" not in template map') % inst.args[0])
399 except IOError, inst:
403 except IOError, inst:
400 raise IOError(inst.args[0], _('template file %s: %s') %
404 raise IOError(inst.args[0], _('template file %s: %s') %
401 (self.map[t][1], inst.args[1]))
405 (self.map[t][1], inst.args[1]))
402 return self.cache[t]
406 return self.cache[t]
403
407
404 def __call__(self, t, **mapping):
408 def __call__(self, t, **mapping):
405 ttype = t in self.map and self.map[t][0] or 'default'
409 ttype = t in self.map and self.map[t][0] or 'default'
406 if ttype not in self.ecache:
410 if ttype not in self.ecache:
407 self.ecache[ttype] = engines[ttype](self.load,
411 self.ecache[ttype] = engines[ttype](self.load,
408 self.filters, self.defaults)
412 self.filters, self.defaults)
409 proc = self.ecache[ttype]
413 proc = self.ecache[ttype]
410
414
411 stream = proc.process(t, mapping)
415 stream = proc.process(t, mapping)
412 if self.minchunk:
416 if self.minchunk:
413 stream = util.increasingchunks(stream, min=self.minchunk,
417 stream = util.increasingchunks(stream, min=self.minchunk,
414 max=self.maxchunk)
418 max=self.maxchunk)
415 return stream
419 return stream
416
420
417 def templatepath(name=None):
421 def templatepath(name=None):
418 '''return location of template file or directory (if no name).
422 '''return location of template file or directory (if no name).
419 returns None if not found.'''
423 returns None if not found.'''
420 normpaths = []
424 normpaths = []
421
425
422 # executable version (py2exe) doesn't support __file__
426 # executable version (py2exe) doesn't support __file__
423 if util.mainfrozen():
427 if util.mainfrozen():
424 module = sys.executable
428 module = sys.executable
425 else:
429 else:
426 module = __file__
430 module = __file__
427 for f in path:
431 for f in path:
428 if f.startswith('/'):
432 if f.startswith('/'):
429 p = f
433 p = f
430 else:
434 else:
431 fl = f.split('/')
435 fl = f.split('/')
432 p = os.path.join(os.path.dirname(module), *fl)
436 p = os.path.join(os.path.dirname(module), *fl)
433 if name:
437 if name:
434 p = os.path.join(p, name)
438 p = os.path.join(p, name)
435 if name and os.path.exists(p):
439 if name and os.path.exists(p):
436 return os.path.normpath(p)
440 return os.path.normpath(p)
437 elif os.path.isdir(p):
441 elif os.path.isdir(p):
438 normpaths.append(os.path.normpath(p))
442 normpaths.append(os.path.normpath(p))
439
443
440 return normpaths
444 return normpaths
441
445
442 def stylemap(styles, paths=None):
446 def stylemap(styles, paths=None):
443 """Return path to mapfile for a given style.
447 """Return path to mapfile for a given style.
444
448
445 Searches mapfile in the following locations:
449 Searches mapfile in the following locations:
446 1. templatepath/style/map
450 1. templatepath/style/map
447 2. templatepath/map-style
451 2. templatepath/map-style
448 3. templatepath/map
452 3. templatepath/map
449 """
453 """
450
454
451 if paths is None:
455 if paths is None:
452 paths = templatepath()
456 paths = templatepath()
453 elif isinstance(paths, str):
457 elif isinstance(paths, str):
454 paths = [paths]
458 paths = [paths]
455
459
456 if isinstance(styles, str):
460 if isinstance(styles, str):
457 styles = [styles]
461 styles = [styles]
458
462
459 for style in styles:
463 for style in styles:
460 if not style:
464 if not style:
461 continue
465 continue
462 locations = [os.path.join(style, 'map'), 'map-' + style]
466 locations = [os.path.join(style, 'map'), 'map-' + style]
463 locations.append('map')
467 locations.append('map')
464
468
465 for path in paths:
469 for path in paths:
466 for location in locations:
470 for location in locations:
467 mapfile = os.path.join(path, location)
471 mapfile = os.path.join(path, location)
468 if os.path.isfile(mapfile):
472 if os.path.isfile(mapfile):
469 return style, mapfile
473 return style, mapfile
470
474
471 raise RuntimeError("No hgweb templates found in %r" % paths)
475 raise RuntimeError("No hgweb templates found in %r" % paths)
General Comments 0
You need to be logged in to leave comments. Login now