comparison markup/eval.py @ 1:5479aae32f5a trunk

Initial import.
author cmlenz
date Sat, 03 Jun 2006 07:16:01 +0000
parents
children 798ebd2425dc
comparison
equal deleted inserted replaced
0:5f9862282a9d 1:5479aae32f5a
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2006 Christopher Lenz
4 # All rights reserved.
5 #
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://trac.edgewall.com/license.html.
9 #
10 # This software consists of voluntary contributions made by many
11 # individuals. For the exact contribution history, see the revision
12 # history and logs, available at http://projects.edgewall.com/trac/.
13
14 import __builtin__
15 import compiler
16 import operator
17
18 from markup.core import Stream
19
20 __all__ = ['Expression']
21
22
23 class Expression(object):
24 """Evaluates Python expressions used in templates.
25
26 >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'})
27 >>> Expression('test').evaluate(data)
28 'Foo'
29 >>> Expression('items[0]').evaluate(data)
30 1
31 >>> Expression('items[-1]').evaluate(data)
32 3
33 >>> Expression('dict["some"]').evaluate(data)
34 'thing'
35
36 Similar to e.g. Javascript, expressions in templates can use the dot
37 notation for attribute access to access items in mappings:
38
39 >>> Expression('dict.some').evaluate(data)
40 'thing'
41
42 This also works the other way around: item access can be used to access
43 any object attribute (meaning there's no use for `getattr()` in templates):
44
45 >>> class MyClass(object):
46 ... myattr = 'Bar'
47 >>> data = dict(mine=MyClass(), key='myattr')
48 >>> Expression('mine.myattr').evaluate(data)
49 'Bar'
50 >>> Expression('mine["myattr"]').evaluate(data)
51 'Bar'
52 >>> Expression('mine[key]').evaluate(data)
53 'Bar'
54
55 Most of the standard Python operators are also available to template
56 expressions. Bitwise operators (including inversion and shifting) are not
57 supported.
58
59 >>> Expression('1 + 1').evaluate(data)
60 2
61 >>> Expression('3 - 1').evaluate(data)
62 2
63 >>> Expression('1 * 2').evaluate(data)
64 2
65 >>> Expression('4 / 2').evaluate(data)
66 2
67 >>> Expression('4 // 3').evaluate(data)
68 1
69 >>> Expression('4 % 3').evaluate(data)
70 1
71 >>> Expression('2 ** 3').evaluate(data)
72 8
73 >>> Expression('not True').evaluate(data)
74 False
75 >>> Expression('True and False').evaluate(data)
76 False
77 >>> Expression('True or False').evaluate(data)
78 True
79 >>> Expression('1 == 3').evaluate(data)
80 False
81 >>> Expression('1 != 3 == 3').evaluate(data)
82 True
83
84 Built-in functions such as `len()` are also available in template
85 expressions:
86
87 >>> data = dict(items=[1, 2, 3])
88 >>> Expression('len(items)').evaluate(data)
89 3
90 """
91 __slots__ = ['source', 'ast']
92 __visitors = {}
93
94 def __init__(self, source):
95 self.source = source
96 self.ast = None
97
98 def evaluate(self, data, default=None):
99 if not self.ast:
100 self.ast = compiler.parse(self.source, 'eval')
101 retval = self._visit(self.ast.node, data)
102 if retval is not None:
103 return retval
104 return default
105
106 def __repr__(self):
107 return '<Expression "%s">' % self.source
108
109 # AST traversal
110
111 def _visit(self, node, data):
112 v = self.__visitors.get(node.__class__)
113 if not v:
114 v = getattr(self, '_visit_%s' % node.__class__.__name__.lower())
115 self.__visitors[node.__class__] = v
116 return v(node, data)
117
118 def _visit_expression(self, node, data):
119 for child in node.getChildNodes():
120 return self._visit(child, data)
121
122 # Functions & Accessors
123
124 def _visit_callfunc(self, node, data):
125 func = self._visit(node.node, data)
126 if func is None:
127 return None
128 args = [self._visit(arg, data) for arg in node.args
129 if not isinstance(arg, compiler.ast.Keyword)]
130 kwargs = dict([(arg.name, self._visit(arg.expr, data)) for arg
131 in node.args if isinstance(arg, compiler.ast.Keyword)])
132 return func(*args, **kwargs)
133
134 def _visit_getattr(self, node, data):
135 obj = self._visit(node.expr, data)
136 try:
137 return getattr(obj, node.attrname)
138 except AttributeError, e:
139 try:
140 return obj[node.attrname]
141 except (KeyError, TypeError):
142 return None
143
144 def _visit_slice(self, node, data):
145 obj = self._visit(node.expr, data)
146 lower = node.lower and self._visit(node.lower, data) or None
147 upper = node.upper and self._visit(node.upper, data) or None
148 return obj[lower:upper]
149
150 def _visit_subscript(self, node, data):
151 obj = self._visit(node.expr, data)
152 subs = map(lambda sub: self._visit(sub, data), node.subs)
153 if len(subs) == 1:
154 subs = subs[0]
155 try:
156 return obj[subs]
157 except (KeyError, IndexError, TypeError):
158 try:
159 return getattr(obj, subs)
160 except (AttributeError, TypeError):
161 return None
162
163 # Operators
164
165 def _visit_and(self, node, data):
166 return reduce(operator.and_, [self._visit(n, data) for n in node.nodes])
167
168 def _visit_or(self, node, data):
169 return reduce(operator.or_, [self._visit(n, data) for n in node.nodes])
170
171 _OP_MAP = {'==': operator.eq, '!=': operator.ne,
172 '<': operator.lt, '<=': operator.le,
173 '>': operator.gt, '>=': operator.ge,
174 'in': operator.contains}
175 def _visit_compare(self, node, data):
176 result = self._visit(node.expr, data)
177 ops = node.ops[:]
178 ops.reverse()
179 for op, rval in ops:
180 result = self._OP_MAP[op](result, self._visit(rval, data))
181 return result
182
183 def _visit_add(self, node, data):
184 return self._visit(node.left, data) + self._visit(node.right, data)
185
186 def _visit_div(self, node, data):
187 return self._visit(node.left, data) / self._visit(node.right, data)
188
189 def _visit_floordiv(self, node, data):
190 return self._visit(node.left, data) // self._visit(node.right, data)
191
192 def _visit_mod(self, node, data):
193 return self._visit(node.left, data) % self._visit(node.right, data)
194
195 def _visit_mul(self, node, data):
196 return self._visit(node.left, data) * self._visit(node.right, data)
197
198 def _visit_power(self, node, data):
199 return self._visit(node.left, data) ** self._visit(node.right, data)
200
201 def _visit_sub(self, node, data):
202 return self._visit(node.left, data) - self._visit(node.right, data)
203
204 def _visit_not(self, node, data):
205 return not self._visit(node.expr, data)
206
207 def _visit_unaryadd(self, node, data):
208 return +self._visit(node.expr, data)
209
210 def _visit_unarysub(self, node, data):
211 return -self._visit(node.expr, data)
212
213 # Identifiers & Literals
214
215 def _visit_name(self, node, data):
216 val = data.get(node.name)
217 if val is None:
218 val = getattr(__builtin__, node.name, None)
219 return val
220
221 def _visit_const(self, node, data):
222 return node.value
223
224 def _visit_dict(self, node, data):
225 return dict([(self._visit(k, data), self._visit(v, data))
226 for k, v in node.items])
227
228 def _visit_tuple(self, node, data):
229 return tuple([self._visit(n, data) for n in node.nodes])
230
231 def _visit_list(self, node, data):
232 return [self._visit(n, data) for n in node.nodes]
Copyright (C) 2012-2017 Edgewall Software