comparison roundup/cgi/PageTemplates/TALES.py @ 1356:83f33642d220 maint-0.5

[[Metadata associated with this commit was garbled during conversion from CVS to Subversion.]]
author Richard Jones <richard@users.sourceforge.net>
date Thu, 09 Jan 2003 22:59:22 +0000
parents
children d6177306d010
comparison
equal deleted inserted replaced
1242:3d0158c8c32b 1356:83f33642d220
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE
11 #
12 ##############################################################################
13 """TALES
14
15 An implementation of a generic TALES engine
16
17 Modified for Roundup 0.5 release:
18
19 - changed imports to import from roundup.cgi
20 """
21
22 __version__='$Revision: 1.4 $'[11:-2]
23
24 import re, sys
25 from roundup.cgi import ZTUtils
26 from MultiMapping import MultiMapping
27
28 StringType = type('')
29
30 NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
31 _parse_expr = re.compile(r"(%s):" % NAME_RE).match
32 _valid_name = re.compile('%s$' % NAME_RE).match
33
34 class TALESError(Exception):
35 """Error during TALES expression evaluation"""
36
37 class Undefined(TALESError):
38 '''Exception raised on traversal of an undefined path'''
39
40 class RegistrationError(Exception):
41 '''TALES Type Registration Error'''
42
43 class CompilerError(Exception):
44 '''TALES Compiler Error'''
45
46 class Default:
47 '''Retain Default'''
48 Default = Default()
49
50 _marker = []
51
52 class SafeMapping(MultiMapping):
53 '''Mapping with security declarations and limited method exposure.
54
55 Since it subclasses MultiMapping, this class can be used to wrap
56 one or more mapping objects. Restricted Python code will not be
57 able to mutate the SafeMapping or the wrapped mappings, but will be
58 able to read any value.
59 '''
60 __allow_access_to_unprotected_subobjects__ = 1
61 push = pop = None
62
63 _push = MultiMapping.push
64 _pop = MultiMapping.pop
65
66 def has_get(self, key, _marker=[]):
67 v = self.get(key, _marker)
68 return v is not _marker, v
69
70 class Iterator(ZTUtils.Iterator):
71 def __init__(self, name, seq, context):
72 ZTUtils.Iterator.__init__(self, seq)
73 self.name = name
74 self._context = context
75
76 def next(self):
77 if ZTUtils.Iterator.next(self):
78 self._context.setLocal(self.name, self.item)
79 return 1
80 return 0
81
82
83 class ErrorInfo:
84 """Information about an exception passed to an on-error handler."""
85 __allow_access_to_unprotected_subobjects__ = 1
86
87 def __init__(self, err, position=(None, None)):
88 if isinstance(err, Exception):
89 self.type = err.__class__
90 self.value = err
91 else:
92 self.type = err
93 self.value = None
94 self.lineno = position[0]
95 self.offset = position[1]
96
97
98 class Engine:
99 '''Expression Engine
100
101 An instance of this class keeps a mutable collection of expression
102 type handlers. It can compile expression strings by delegating to
103 these handlers. It can provide an expression Context, which is
104 capable of holding state and evaluating compiled expressions.
105 '''
106 Iterator = Iterator
107
108 def __init__(self, Iterator=None):
109 self.types = {}
110 if Iterator is not None:
111 self.Iterator = Iterator
112
113 def registerType(self, name, handler):
114 if not _valid_name(name):
115 raise RegistrationError, 'Invalid Expression type "%s".' % name
116 types = self.types
117 if types.has_key(name):
118 raise RegistrationError, (
119 'Multiple registrations for Expression type "%s".' %
120 name)
121 types[name] = handler
122
123 def getTypes(self):
124 return self.types
125
126 def compile(self, expression):
127 m = _parse_expr(expression)
128 if m:
129 type = m.group(1)
130 expr = expression[m.end():]
131 else:
132 type = "standard"
133 expr = expression
134 try:
135 handler = self.types[type]
136 except KeyError:
137 raise CompilerError, (
138 'Unrecognized expression type "%s".' % type)
139 return handler(type, expr, self)
140
141 def getContext(self, contexts=None, **kwcontexts):
142 if contexts is not None:
143 if kwcontexts:
144 kwcontexts.update(contexts)
145 else:
146 kwcontexts = contexts
147 return Context(self, kwcontexts)
148
149 def getCompilerError(self):
150 return CompilerError
151
152 class Context:
153 '''Expression Context
154
155 An instance of this class holds context information that it can
156 use to evaluate compiled expressions.
157 '''
158
159 _context_class = SafeMapping
160 position = (None, None)
161 source_file = None
162
163 def __init__(self, compiler, contexts):
164 self._compiler = compiler
165 self.contexts = contexts
166 contexts['nothing'] = None
167 contexts['default'] = Default
168
169 self.repeat_vars = rv = {}
170 # Wrap this, as it is visible to restricted code
171 contexts['repeat'] = rep = self._context_class(rv)
172 contexts['loop'] = rep # alias
173
174 self.global_vars = gv = contexts.copy()
175 self.local_vars = lv = {}
176 self.vars = self._context_class(gv, lv)
177
178 # Keep track of what needs to be popped as each scope ends.
179 self._scope_stack = []
180
181 def getCompiler(self):
182 return self._compiler
183
184 def beginScope(self):
185 self._scope_stack.append([self.local_vars.copy()])
186
187 def endScope(self):
188 scope = self._scope_stack.pop()
189 self.local_vars = lv = scope[0]
190 v = self.vars
191 v._pop()
192 v._push(lv)
193 # Pop repeat variables, if any
194 i = len(scope) - 1
195 while i:
196 name, value = scope[i]
197 if value is None:
198 del self.repeat_vars[name]
199 else:
200 self.repeat_vars[name] = value
201 i = i - 1
202
203 def setLocal(self, name, value):
204 self.local_vars[name] = value
205
206 def setGlobal(self, name, value):
207 self.global_vars[name] = value
208
209 def setRepeat(self, name, expr):
210 expr = self.evaluate(expr)
211 if not expr:
212 return self._compiler.Iterator(name, (), self)
213 it = self._compiler.Iterator(name, expr, self)
214 old_value = self.repeat_vars.get(name)
215 self._scope_stack[-1].append((name, old_value))
216 self.repeat_vars[name] = it
217 return it
218
219 def evaluate(self, expression,
220 isinstance=isinstance, StringType=StringType):
221 if isinstance(expression, StringType):
222 expression = self._compiler.compile(expression)
223 __traceback_supplement__ = (
224 TALESTracebackSupplement, self, expression)
225 v = expression(self)
226 return v
227
228 evaluateValue = evaluate
229 evaluateBoolean = evaluate
230
231 def evaluateText(self, expr, None=None):
232 text = self.evaluate(expr)
233 if text is Default or text is None:
234 return text
235 return str(text)
236
237 def evaluateStructure(self, expr):
238 return self.evaluate(expr)
239 evaluateStructure = evaluate
240
241 def evaluateMacro(self, expr):
242 # XXX Should return None or a macro definition
243 return self.evaluate(expr)
244 evaluateMacro = evaluate
245
246 def createErrorInfo(self, err, position):
247 return ErrorInfo(err, position)
248
249 def getDefault(self):
250 return Default
251
252 def setSourceFile(self, source_file):
253 self.source_file = source_file
254
255 def setPosition(self, position):
256 self.position = position
257
258
259
260 class TALESTracebackSupplement:
261 """Implementation of ITracebackSupplement"""
262 def __init__(self, context, expression):
263 self.context = context
264 self.source_url = context.source_file
265 self.line = context.position[0]
266 self.column = context.position[1]
267 self.expression = repr(expression)
268
269 def getInfo(self, as_html=0):
270 import pprint
271 data = self.context.contexts.copy()
272 s = pprint.pformat(data)
273 if not as_html:
274 return ' - Names:\n %s' % string.replace(s, '\n', '\n ')
275 else:
276 from cgi import escape
277 return '<b>Names:</b><pre>%s</pre>' % (escape(s))
278 return None
279
280
281
282 class SimpleExpr:
283 '''Simple example of an expression type handler'''
284 def __init__(self, name, expr, engine):
285 self._name = name
286 self._expr = expr
287 def __call__(self, econtext):
288 return self._name, self._expr
289 def __repr__(self):
290 return '<SimpleExpr %s %s>' % (self._name, `self._expr`)
291

Roundup Issue Tracker: http://roundup-tracker.org/