##// END OF EJS Templates
improve templating....
Vadim Gelfer -
r1904:a7e416bf default
parent child Browse files
Show More
@@ -1,159 +1,175 b''
1 import re
1 import re
2 from demandload import demandload
2 from demandload import demandload
3 from i18n import gettext as _
3 from i18n import gettext as _
4 demandload(globals(), "cStringIO cgi os time urllib util")
4 demandload(globals(), "cStringIO cgi os time urllib util")
5
5
6 esctable = {
6 esctable = {
7 '\\': '\\',
7 '\\': '\\',
8 'r': '\r',
8 'r': '\r',
9 't': '\t',
9 't': '\t',
10 'n': '\n',
10 'n': '\n',
11 'v': '\v',
11 'v': '\v',
12 }
12 }
13
13
14 def parsestring(s, quoted=True):
14 def parsestring(s, quoted=True):
15 fp = cStringIO.StringIO()
15 fp = cStringIO.StringIO()
16 if quoted:
16 if quoted:
17 first = s[0]
17 first = s[0]
18 if len(s) < 2: raise SyntaxError(_('string too short'))
18 if len(s) < 2: raise SyntaxError(_('string too short'))
19 if first not in "'\"": raise SyntaxError(_('invalid quote'))
19 if first not in "'\"": raise SyntaxError(_('invalid quote'))
20 if s[-1] != first: raise SyntaxError(_('unmatched quotes'))
20 if s[-1] != first: raise SyntaxError(_('unmatched quotes'))
21 s = s[1:-1]
21 s = s[1:-1]
22 escape = False
22 escape = False
23 for c in s:
23 for c in s:
24 if escape:
24 if escape:
25 fp.write(esctable.get(c, c))
25 fp.write(esctable.get(c, c))
26 escape = False
26 escape = False
27 elif c == '\\': escape = True
27 elif c == '\\': escape = True
28 elif quoted and c == first: raise SyntaxError(_('string ends early'))
28 elif quoted and c == first: raise SyntaxError(_('string ends early'))
29 else: fp.write(c)
29 else: fp.write(c)
30 if escape: raise SyntaxError(_('unterminated escape'))
30 if escape: raise SyntaxError(_('unterminated escape'))
31 return fp.getvalue()
31 return fp.getvalue()
32
32
33 class templater(object):
33 class templater(object):
34 def __init__(self, mapfile, filters={}, defaults={}):
34 def __init__(self, mapfile, filters={}, defaults={}):
35 self.cache = {}
35 self.cache = {}
36 self.map = {}
36 self.map = {}
37 self.base = os.path.dirname(mapfile)
37 self.base = os.path.dirname(mapfile)
38 self.filters = filters
38 self.filters = filters
39 self.defaults = defaults
39 self.defaults = defaults
40
40
41 i = 0
41 i = 0
42 for l in file(mapfile):
42 for l in file(mapfile):
43 i += 1
43 i += 1
44 m = re.match(r'(\S+)\s*=\s*(["\'].*["\'])$', l)
44 m = re.match(r'(\S+)\s*=\s*(["\'].*["\'])$', l)
45 if m:
45 if m:
46 try:
46 try:
47 s = m.group(2)
47 s = m.group(2)
48 self.cache[m.group(1)] = parsestring(s)
48 self.cache[m.group(1)] = parsestring(s)
49 except SyntaxError, inst:
49 except SyntaxError, inst:
50 raise SyntaxError('%s:%s: %s' % (mapfile, i, inst.args[0]))
50 raise SyntaxError('%s:%s: %s' % (mapfile, i, inst.args[0]))
51 else:
51 else:
52 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
52 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
53 if m:
53 if m:
54 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
54 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
55 else:
55 else:
56 raise LookupError(_("unknown map entry '%s'") % l)
56 raise LookupError(_("unknown map entry '%s'") % l)
57
57
58 def __contains__(self, key):
58 def __contains__(self, key):
59 return key in self.cache
59 return key in self.cache
60
60
61 def __call__(self, t, **map):
61 def __call__(self, t, **map):
62 m = self.defaults.copy()
62 m = self.defaults.copy()
63 m.update(map)
63 m.update(map)
64 try:
64 try:
65 tmpl = self.cache[t]
65 tmpl = self.cache[t]
66 except KeyError:
66 except KeyError:
67 tmpl = self.cache[t] = file(self.map[t]).read()
67 tmpl = self.cache[t] = file(self.map[t]).read()
68 return self.template(tmpl, self.filters, **m)
68 return self.template(tmpl, self.filters, **m)
69
69
70 template_re = re.compile(r"#([a-zA-Z_][a-zA-Z0-9_]*)"
70 template_re = re.compile(r"[#{]([a-zA-Z_][a-zA-Z0-9_]*)"
71 r"((%[a-zA-Z_][a-zA-Z0-9_]*)*)"
71 r"((%[a-zA-Z_][a-zA-Z0-9_]*)*)"
72 r"((\|[a-zA-Z_][a-zA-Z0-9_]*)*)#")
72 r"((\|[a-zA-Z_][a-zA-Z0-9_]*)*)[#}]")
73
73
74 def template(self, tmpl, filters={}, **map):
74 def template(self, tmpl, filters={}, **map):
75 lm = map.copy()
75 lm = map.copy()
76 while tmpl:
76 while tmpl:
77 m = self.template_re.search(tmpl)
77 m = self.template_re.search(tmpl)
78 if m:
78 if m:
79 start = m.start(0)
79 start = m.start(0)
80 if start:
80 if start:
81 yield tmpl[:start]
81 yield tmpl[:start]
82 v = map.get(m.group(1), "")
82 v = map.get(m.group(1), "")
83 v = callable(v) and v(**map) or v
83 v = callable(v) and v(**map) or v
84
84
85 format = m.group(2)
85 format = m.group(2)
86 fl = m.group(4)
86 fl = m.group(4)
87
87
88 if format:
88 if format:
89 q = v.__iter__
89 q = v.__iter__
90 for i in q():
90 for i in q():
91 lm.update(i)
91 lm.update(i)
92 yield self(format[1:], **lm)
92 yield self(format[1:], **lm)
93
93
94 v = ""
94 v = ""
95
95
96 elif fl:
96 elif fl:
97 for f in fl.split("|")[1:]:
97 for f in fl.split("|")[1:]:
98 v = filters[f](v)
98 v = filters[f](v)
99
99
100 yield v
100 yield v
101 tmpl = tmpl[m.end(0):]
101 tmpl = tmpl[m.end(0):]
102 else:
102 else:
103 yield tmpl
103 yield tmpl
104 break
104 break
105
105
106 agescales = [("second", 1),
106 agescales = [("second", 1),
107 ("minute", 60),
107 ("minute", 60),
108 ("hour", 3600),
108 ("hour", 3600),
109 ("day", 3600 * 24),
109 ("day", 3600 * 24),
110 ("week", 3600 * 24 * 7),
110 ("week", 3600 * 24 * 7),
111 ("month", 3600 * 24 * 30),
111 ("month", 3600 * 24 * 30),
112 ("year", 3600 * 24 * 365)]
112 ("year", 3600 * 24 * 365)]
113
113
114 agescales.reverse()
114 agescales.reverse()
115
115
116 def age(x):
116 def age(x):
117 def plural(t, c):
117 def plural(t, c):
118 if c == 1:
118 if c == 1:
119 return t
119 return t
120 return t + "s"
120 return t + "s"
121 def fmt(t, c):
121 def fmt(t, c):
122 return "%d %s" % (c, plural(t, c))
122 return "%d %s" % (c, plural(t, c))
123
123
124 now = time.time()
124 now = time.time()
125 then = x[0]
125 then = x[0]
126 delta = max(1, int(now - then))
126 delta = max(1, int(now - then))
127
127
128 for t, s in agescales:
128 for t, s in agescales:
129 n = delta / s
129 n = delta / s
130 if n >= 2 or s == 1:
130 if n >= 2 or s == 1:
131 return fmt(t, n)
131 return fmt(t, n)
132
132
133 def nl2br(text):
133 def nl2br(text):
134 return text.replace('\n', '<br/>\n')
134 return text.replace('\n', '<br/>\n')
135
135
136 def obfuscate(text):
136 def obfuscate(text):
137 return ''.join(['&#%d;' % ord(c) for c in text])
137 return ''.join(['&#%d;' % ord(c) for c in text])
138
138
139 def domain(author):
140 f = author.find('@')
141 if f == -1: return ''
142 author = author[f+1:]
143 f = author.find('>')
144 if f >= 0: author = author[:f]
145 return author
146
147 def person(author):
148 f = author.find('<')
149 if f == -1: return util.shortuser(author)
150 return author[:f].rstrip()
151
139 common_filters = {
152 common_filters = {
140 "escape": lambda x: cgi.escape(x, True),
153 "addbreaks": nl2br,
141 "urlescape": urllib.quote,
142 "strip": lambda x: x.strip(),
143 "age": age,
154 "age": age,
144 "date": lambda x: util.datestr(x),
155 "date": lambda x: util.datestr(x),
145 "addbreaks": nl2br,
156 "escape": lambda x: cgi.escape(x, True),
157 "firstline": (lambda x: x.splitlines(1)[0]),
158 "domain": domain,
146 "obfuscate": obfuscate,
159 "obfuscate": obfuscate,
160 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
161 "person": person,
162 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
147 "short": (lambda x: x[:12]),
163 "short": (lambda x: x[:12]),
148 "firstline": (lambda x: x.splitlines(1)[0]),
164 "strip": lambda x: x.strip(),
149 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
165 "urlescape": urllib.quote,
150 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
166 "user": util.shortuser,
151 }
167 }
152
168
153 def templatepath(name=None):
169 def templatepath(name=None):
154 for f in 'templates', '../templates':
170 for f in 'templates', '../templates':
155 fl = f.split('/')
171 fl = f.split('/')
156 if name: fl.append(name)
172 if name: fl.append(name)
157 p = os.path.join(os.path.dirname(__file__), *fl)
173 p = os.path.join(os.path.dirname(__file__), *fl)
158 if (name and os.path.exists(p)) or os.path.isdir(p):
174 if (name and os.path.exists(p)) or os.path.isdir(p):
159 return os.path.normpath(p)
175 return os.path.normpath(p)
General Comments 0
You need to be logged in to leave comments. Login now