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