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