Mercurial > p > roundup > code
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 |
