Mercurial > p > roundup > code
annotate roundup/cgi/PageTemplates/TALES.py @ 8241:741ea8a86012
fix: issue2551374. Error handling for filter expressions.
Errors in filter expressions are now reported. The UI needs some work
but even the current code is helpful when debugging filter
expressions.
mlink_expr:
defines/raises ExpressionError(error string template,
context=dict())
raises ExpressionError when it detects errors when popping arguments
off stack
raises ExpressionError when more than one element left on the stack
before returning
also ruff fix to group boolean expression with parens
back_anydbm.py, rdbms_common.py:
catches ExpressionError, augments context with class and
attribute being searched. raises the exception
for both link and multilink relations
client.py
catches ExpressionError returning a basic error page. The page is a
dead end. There are no links or anything for the user to move
forward. The user has to go back, possibly refresh the page (because
the submit button may be disalbled) re-enter the query and try
again.
This needs to be improved.
test_liveserver.py
test the error page generated by client.py
db_test_base
unit tests for filter with too few arguments, too many arguments,
check all repr and str formats.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Mon, 30 Dec 2024 20:22:55 -0500 |
| parents | bd4097fa0671 |
| children |
| rev | line source |
|---|---|
| 1049 | 1 ############################################################################## |
| 2 # | |
| 3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
4 # |
| 1049 | 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 | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
11 # |
|
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
12 ############################################################################## |
|
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
13 # Modified for Roundup: |
| 1049 | 14 # |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
15 # 1. changed imports to import from roundup.cgi |
|
2663
5f9e00836006
update comment
Richard Jones <richard@users.sourceforge.net>
parents:
2662
diff
changeset
|
16 # 2. implemented ustr as str (removes import from DocumentTemplate) |
|
5f9e00836006
update comment
Richard Jones <richard@users.sourceforge.net>
parents:
2662
diff
changeset
|
17 # 3. removed import and use of Unauthorized from zExceptions |
| 1049 | 18 """TALES |
| 19 | |
| 20 An implementation of a generic TALES engine | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
21 """ |
|
1071
c08b3820edd1
Adhering to ZPL
Richard Jones <richard@users.sourceforge.net>
parents:
1049
diff
changeset
|
22 |
| 1049 | 23 import re, sys |
| 24 from roundup.cgi import ZTUtils | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
25 from weakref import ref |
|
5388
d26921b851c3
Python 3 preparation: make relative imports explicit.
Joseph Myers <jsm@polyomino.org.uk>
parents:
5381
diff
changeset
|
26 from .MultiMapping import MultiMapping |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
27 |
|
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
28 ustr = str |
| 1049 | 29 |
| 30 StringType = type('') | |
| 31 | |
| 32 NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*" | |
| 33 _parse_expr = re.compile(r"(%s):" % NAME_RE).match | |
| 34 _valid_name = re.compile('%s$' % NAME_RE).match | |
| 35 | |
|
5265
63868084b8bb
Python 2 and 3 support. Convert Exception to BaseException. TAL and
John Rouillard <rouilj@ieee.org>
parents:
4570
diff
changeset
|
36 class TALESError(BaseException): |
| 1049 | 37 """Error during TALES expression evaluation""" |
| 38 | |
| 39 class Undefined(TALESError): | |
| 40 '''Exception raised on traversal of an undefined path''' | |
| 41 | |
|
5265
63868084b8bb
Python 2 and 3 support. Convert Exception to BaseException. TAL and
John Rouillard <rouilj@ieee.org>
parents:
4570
diff
changeset
|
42 class RegistrationError(BaseException): |
| 1049 | 43 '''TALES Type Registration Error''' |
| 44 | |
|
5265
63868084b8bb
Python 2 and 3 support. Convert Exception to BaseException. TAL and
John Rouillard <rouilj@ieee.org>
parents:
4570
diff
changeset
|
45 class CompilerError(BaseException): |
| 1049 | 46 '''TALES Compiler Error''' |
| 47 | |
| 48 class Default: | |
| 49 '''Retain Default''' | |
| 50 Default = Default() | |
| 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 | |
| 67 class Iterator(ZTUtils.Iterator): | |
| 68 def __init__(self, name, seq, context): | |
| 69 ZTUtils.Iterator.__init__(self, seq) | |
| 70 self.name = name | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
71 self._context_ref = ref(context) |
| 1049 | 72 |
| 73 def next(self): | |
| 74 if ZTUtils.Iterator.next(self): | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
75 context = self._context_ref() |
|
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
76 if context is not None: |
|
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
77 context.setLocal(self.name, self.item) |
| 1049 | 78 return 1 |
| 79 return 0 | |
| 80 | |
| 81 | |
| 82 class ErrorInfo: | |
| 83 """Information about an exception passed to an on-error handler.""" | |
| 84 __allow_access_to_unprotected_subobjects__ = 1 | |
| 85 | |
| 86 def __init__(self, err, position=(None, None)): | |
|
5265
63868084b8bb
Python 2 and 3 support. Convert Exception to BaseException. TAL and
John Rouillard <rouilj@ieee.org>
parents:
4570
diff
changeset
|
87 if isinstance(err, BaseException): |
| 1049 | 88 self.type = err.__class__ |
| 89 self.value = err | |
| 90 else: | |
| 91 self.type = err | |
| 92 self.value = None | |
| 93 self.lineno = position[0] | |
| 94 self.offset = position[1] | |
| 95 | |
| 96 | |
| 97 class Engine: | |
| 98 '''Expression Engine | |
| 99 | |
| 100 An instance of this class keeps a mutable collection of expression | |
| 101 type handlers. It can compile expression strings by delegating to | |
| 102 these handlers. It can provide an expression Context, which is | |
| 103 capable of holding state and evaluating compiled expressions. | |
| 104 ''' | |
| 105 Iterator = Iterator | |
| 106 | |
| 107 def __init__(self, Iterator=None): | |
| 108 self.types = {} | |
| 109 if Iterator is not None: | |
| 110 self.Iterator = Iterator | |
| 111 | |
| 112 def registerType(self, name, handler): | |
| 113 if not _valid_name(name): | |
|
5378
35ea9b1efc14
Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents:
5377
diff
changeset
|
114 raise RegistrationError('Invalid Expression type "%s".' % name) |
| 1049 | 115 types = self.types |
|
5381
0942fe89e82e
Python 3 preparation: change "x.has_key(y)" to "y in x".
Joseph Myers <jsm@polyomino.org.uk>
parents:
5378
diff
changeset
|
116 if name in types: |
|
5378
35ea9b1efc14
Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents:
5377
diff
changeset
|
117 raise RegistrationError( |
| 1049 | 118 'Multiple registrations for Expression type "%s".' % |
| 119 name) | |
| 120 types[name] = handler | |
| 121 | |
| 122 def getTypes(self): | |
| 123 return self.types | |
| 124 | |
| 125 def compile(self, expression): | |
| 126 m = _parse_expr(expression) | |
| 127 if m: | |
| 128 type = m.group(1) | |
| 129 expr = expression[m.end():] | |
| 130 else: | |
| 131 type = "standard" | |
| 132 expr = expression | |
| 133 try: | |
| 134 handler = self.types[type] | |
| 135 except KeyError: | |
|
5378
35ea9b1efc14
Python 3 preparation: "raise" syntax.
Joseph Myers <jsm@polyomino.org.uk>
parents:
5377
diff
changeset
|
136 raise CompilerError( |
| 1049 | 137 'Unrecognized expression type "%s".' % type) |
| 138 return handler(type, expr, self) | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
139 |
| 1049 | 140 def getContext(self, contexts=None, **kwcontexts): |
| 141 if contexts is not None: | |
| 142 if kwcontexts: | |
| 143 kwcontexts.update(contexts) | |
| 144 else: | |
| 145 kwcontexts = contexts | |
| 146 return Context(self, kwcontexts) | |
| 147 | |
| 148 def getCompilerError(self): | |
| 149 return CompilerError | |
| 150 | |
| 151 class Context: | |
| 152 '''Expression Context | |
| 153 | |
| 154 An instance of this class holds context information that it can | |
| 155 use to evaluate compiled expressions. | |
| 156 ''' | |
| 157 | |
| 158 _context_class = SafeMapping | |
| 159 position = (None, None) | |
| 160 source_file = None | |
| 161 | |
|
1257
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
162 def __init__(self, compiler, contexts): |
|
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
163 self._compiler = compiler |
| 1049 | 164 self.contexts = contexts |
| 165 contexts['nothing'] = None | |
| 166 contexts['default'] = Default | |
| 167 | |
| 168 self.repeat_vars = rv = {} | |
| 169 # Wrap this, as it is visible to restricted code | |
| 170 contexts['repeat'] = rep = self._context_class(rv) | |
| 171 contexts['loop'] = rep # alias | |
| 172 | |
| 173 self.global_vars = gv = contexts.copy() | |
| 174 self.local_vars = lv = {} | |
| 175 self.vars = self._context_class(gv, lv) | |
| 176 | |
| 177 # Keep track of what needs to be popped as each scope ends. | |
| 178 self._scope_stack = [] | |
| 179 | |
|
1257
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
180 def getCompiler(self): |
|
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
181 return self._compiler |
|
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
182 |
| 1049 | 183 def beginScope(self): |
| 184 self._scope_stack.append([self.local_vars.copy()]) | |
| 185 | |
| 186 def endScope(self): | |
| 187 scope = self._scope_stack.pop() | |
| 188 self.local_vars = lv = scope[0] | |
| 189 v = self.vars | |
| 190 v._pop() | |
| 191 v._push(lv) | |
| 192 # Pop repeat variables, if any | |
| 193 i = len(scope) - 1 | |
| 194 while i: | |
| 195 name, value = scope[i] | |
| 196 if value is None: | |
| 197 del self.repeat_vars[name] | |
| 198 else: | |
| 199 self.repeat_vars[name] = value | |
| 200 i = i - 1 | |
| 201 | |
| 202 def setLocal(self, name, value): | |
| 203 self.local_vars[name] = value | |
| 204 | |
| 205 def setGlobal(self, name, value): | |
| 206 self.global_vars[name] = value | |
| 207 | |
| 208 def setRepeat(self, name, expr): | |
| 209 expr = self.evaluate(expr) | |
| 210 if not expr: | |
|
1257
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
211 return self._compiler.Iterator(name, (), self) |
|
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
212 it = self._compiler.Iterator(name, expr, self) |
| 1049 | 213 old_value = self.repeat_vars.get(name) |
| 214 self._scope_stack[-1].append((name, old_value)) | |
| 215 self.repeat_vars[name] = it | |
| 216 return it | |
| 217 | |
| 218 def evaluate(self, expression, | |
| 219 isinstance=isinstance, StringType=StringType): | |
| 220 if isinstance(expression, StringType): | |
|
1257
93b80ad11ca8
merged Zope Collector #372 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1219
diff
changeset
|
221 expression = self._compiler.compile(expression) |
| 1049 | 222 __traceback_supplement__ = ( |
| 223 TALESTracebackSupplement, self, expression) | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
224 return expression(self) |
| 1049 | 225 |
| 226 evaluateValue = evaluate | |
|
1219
9620f6782cb7
merge Zope Collector #580 fix from ZPT CVS trunk
Richard Jones <richard@users.sourceforge.net>
parents:
1071
diff
changeset
|
227 evaluateBoolean = evaluate |
| 1049 | 228 |
|
1409
8dc60d87ab42
Fixed a backlog of bug reports, and worked on python 2.3 compatibility:
Richard Jones <richard@users.sourceforge.net>
parents:
1257
diff
changeset
|
229 def evaluateText(self, expr): |
| 1049 | 230 text = self.evaluate(expr) |
| 231 if text is Default or text is None: | |
| 232 return text | |
|
5416
56c9bcdea47f
Python 3 preparation: unicode.
Joseph Myers <jsm@polyomino.org.uk>
parents:
5388
diff
changeset
|
233 if isinstance(text, type(u'')): |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
234 return text |
|
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
235 else: |
|
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
236 return ustr(text) |
| 1049 | 237 |
| 238 def evaluateStructure(self, expr): | |
| 239 return self.evaluate(expr) | |
| 240 evaluateStructure = evaluate | |
| 241 | |
| 242 def evaluateMacro(self, expr): | |
| 243 # XXX Should return None or a macro definition | |
| 244 return self.evaluate(expr) | |
| 245 evaluateMacro = evaluate | |
| 246 | |
| 247 def createErrorInfo(self, err, position): | |
| 248 return ErrorInfo(err, position) | |
| 249 | |
| 250 def getDefault(self): | |
| 251 return Default | |
| 252 | |
| 253 def setSourceFile(self, source_file): | |
| 254 self.source_file = source_file | |
| 255 | |
| 256 def setPosition(self, position): | |
| 257 self.position = position | |
| 258 | |
| 259 class TALESTracebackSupplement: | |
| 260 """Implementation of ITracebackSupplement""" | |
| 261 def __init__(self, context, expression): | |
| 262 self.context = context | |
| 263 self.source_url = context.source_file | |
| 264 self.line = context.position[0] | |
| 265 self.column = context.position[1] | |
| 266 self.expression = repr(expression) | |
| 267 | |
| 268 def getInfo(self, as_html=0): | |
| 269 import pprint | |
| 270 data = self.context.contexts.copy() | |
| 271 s = pprint.pformat(data) | |
| 272 if not as_html: | |
|
2349
b43efe461b3e
update PageTemplates to latest Zope codebase
Richard Jones <richard@users.sourceforge.net>
parents:
2005
diff
changeset
|
273 return ' - Names:\n %s' % s.replace('\n', '\n ') |
| 1049 | 274 else: |
|
5837
883c9e90b403
Fix problem with cgi.escape being depricated a different way. This way
John Rouillard <rouilj@ieee.org>
parents:
5416
diff
changeset
|
275 from roundup.anypy.html import html_escape |
|
883c9e90b403
Fix problem with cgi.escape being depricated a different way. This way
John Rouillard <rouilj@ieee.org>
parents:
5416
diff
changeset
|
276 return '<b>Names:</b><pre>%s</pre>' % (html_escape(s)) |
| 1049 | 277 |
| 278 | |
| 279 class SimpleExpr: | |
| 280 '''Simple example of an expression type handler''' | |
| 281 def __init__(self, name, expr, engine): | |
| 282 self._name = name | |
| 283 self._expr = expr | |
| 284 def __call__(self, econtext): | |
| 285 return self._name, self._expr | |
| 286 def __repr__(self): | |
|
5377
12fe83f90f0d
Python 3 preparation: use repr() instead of ``.
Joseph Myers <jsm@polyomino.org.uk>
parents:
5265
diff
changeset
|
287 return '<SimpleExpr %s %s>' % (self._name, repr(self._expr)) |
