Commit 0a68cd0d authored by Evan Simpson's avatar Evan Simpson

Improve error reporting.

parent 4e3ab31c
......@@ -89,7 +89,7 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths.
"""
__version__='$Revision: 1.20 $'[11:-2]
__version__='$Revision: 1.21 $'[11:-2]
import re, sys
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
......@@ -114,6 +114,7 @@ def installHandlers(engine):
reg('string', StringExpr)
reg('python', PythonExpr)
reg('not', NotExpr)
reg('defer', DeferExpr)
if sys.modules.has_key('Zope'):
import AccessControl
......@@ -194,10 +195,14 @@ class PathExpr:
ob = econtext.contexts
else:
ob = vars[base]
if isinstance(ob, DeferWrapper):
ob = ob()
if path:
ob = restrictedTraverse(ob, path, securityManager)
exists = 1
break
except Undefined, e:
ob = e
except (AttributeError, KeyError, TypeError, IndexError,
'Unauthorized'), e:
ob = Undefined(self._s, sys.exc_info())
......@@ -214,10 +219,10 @@ class PathExpr:
return self._eval(econtext, getSecurityManager())
def __str__(self):
return '%s expression "%s"' % (self._name, self._s)
return '%s expression %s' % (self._name, `self._s`)
def __repr__(self):
return '<PathExpr %s:%s>' % (self._name, self._s)
return '%s:%s' % (self._name, `self._s`)
_interp = re.compile(r'\$(%(n)s)|\${(%(n)s(?:/%(n)s)*)}' % {'n': NAME_RE})
......@@ -242,8 +247,7 @@ class StringExpr:
m = _interp.search(exp)
if '$' in exp:
raise CompilerError, (
'$ must be doubled or followed by a variable name '
'in string expression "%s"' % expr)
'$ must be doubled or followed by a simple path')
parts.append(exp)
expr = join(parts, '')
self._expr = expr
......@@ -261,7 +265,7 @@ class StringExpr:
return 'string expression %s' % `self._s`
def __repr__(self):
return '<StringExpr %s>' % `self._s`
return 'string:%s' % `self._s`
class NotExpr:
def __init__(self, name, expr, compiler):
......@@ -272,7 +276,29 @@ class NotExpr:
return not econtext.evaluateBoolean(self._c)
def __repr__(self):
return '<NotExpr %s>' % `self._s`
return 'not:%s' % `self._s`
class DeferWrapper:
def __init__(self, expr, econtext):
self._expr = expr
self._econtext = econtext
def __str__(self):
return str(self())
def __call__(self):
return self._expr(self._econtext)
class DeferExpr:
def __init__(self, name, expr, compiler):
self._s = expr = lstrip(expr)
self._c = compiler.compile(expr)
def __call__(self, econtext):
return DeferWrapper(self._c, econtext)
def __repr__(self):
return 'defer:%s' % `self._s`
def restrictedTraverse(self, path, securityManager,
......
......@@ -87,7 +87,7 @@
HTML- and XML-based template objects using TAL, TALES, and METAL.
"""
__version__='$Revision: 1.14 $'[11:-2]
__version__='$Revision: 1.15 $'[11:-2]
import os, sys, traceback, pprint
from TAL.TALParser import TALParser
......@@ -111,6 +111,7 @@ class PageTemplate(Base):
content_type = 'text/html'
expand = 1
_v_errors = ()
_v_warnings = ()
_text = ''
_error_start = '<!-- Page Template Diagnostics'
......@@ -162,7 +163,16 @@ class PageTemplate(Base):
return self.pt_render(extra_context={'options': kwargs})
def pt_errors(self):
return self._v_errors
err = self._v_errors
if err:
return err
try:
self.pt_render(source=1)
except:
return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
def pt_warnings(self):
return self._v_warnings
def write(self, text):
assert type(text) is type('')
......@@ -209,6 +219,7 @@ class PageTemplate(Base):
except:
self._v_errors = ["Compilation failed",
"%s: %s" % sys.exc_info()[:2]]
self._v_warnings = parser.getWarnings()
def html(self):
return self.content_type == 'text/html'
......
......@@ -87,7 +87,7 @@
Zope object encapsulating a Page Template from the filesystem.
"""
__version__='$Revision: 1.3 $'[11:-2]
__version__='$Revision: 1.4 $'[11:-2]
import os, AccessControl, Acquisition, sys
from Globals import package_home, DevelopmentMode
......@@ -157,6 +157,7 @@ class PageTemplateFile(Script, PageTemplate, Traversable):
# Execute the template in a new security context.
security=getSecurityManager()
bound_names['user'] = security.getUser()
security.addContext(self)
try:
return self.pt_render(extra_context=bound_names)
......
......@@ -87,7 +87,7 @@
An implementation of a generic TALES engine
"""
__version__='$Revision: 1.21 $'[11:-2]
__version__='$Revision: 1.22 $'[11:-2]
import re, sys, ZTUtils
from MultiMapping import MultiMapping
......@@ -100,24 +100,41 @@ _valid_name = re.compile('%s$' % NAME_RE).match
class TALESError(Exception):
__allow_access_to_unprotected_subobjects__ = 1
def __init__(self, expression, info=(None, None, None)):
def __init__(self, expression, info=(None, None, None),
position=(None, None)):
self.type, self.value, self.traceback = info
self.expression = expression
self.setPosition(position)
def setPosition(self, position):
self.lineno = position[0]
self.offset = position[1]
def __str__(self):
if self.type is not None:
return '%s on %s in "%s"' % (self.type, self.value,
self.expression)
return self.expression
if self.type is None:
s = self.expression
else:
s = '%s on %s in %s' % (self.type, self.value,
`self.expression`)
if self.lineno is not None:
s = "%s, at line %d" % (s, self.lineno)
if self.offset is not None:
s = "%s, column %d" % (s, self.offset + 1)
return s
def __nonzero__(self):
return 0
class Undefined(TALESError):
'''Exception raised on traversal of an undefined path'''
def __str__(self):
if self.type is not None:
return '"%s" not found in "%s"' % (self.value,
self.expression)
return self.expression
if self.type is None:
s = self.expression
else:
s = '%s not found in %s' % (self.value,
`self.expression`)
if self.lineno is not None:
s = "%s, at line %d" % (s, self.lineno)
if self.offset is not None:
s = "%s, column %d" % (s, self.offset + 1)
return s
class RegistrationError(Exception):
'''TALES Type Registration Error'''
......@@ -221,6 +238,8 @@ class Engine:
kwcontexts = contexts
return Context(self, kwcontexts)
def getCompilerError(self):
return CompilerError
class Context:
'''Expression Context
......@@ -231,6 +250,7 @@ class Context:
_context_class = SafeMapping
_nocatch = TALESError
position = (None, None)
def __init__(self, engine, contexts):
self._engine = engine
......@@ -297,10 +317,14 @@ class Context:
if hasattr(v, 'traceback'):
raise v, None, v.traceback
raise v
except TALESError, err:
err.setPosition(self.position)
raise err, None, sys.exc_info()[2]
except self._nocatch:
raise
except:
raise TALESError, (`expression`, sys.exc_info()), sys.exc_info()[2]
raise TALESError, (`expression`, sys.exc_info(),
self.position), sys.exc_info()[2]
else:
return v
......@@ -330,6 +354,9 @@ class Context:
def getDefault(self):
return Default
def setPosition(self, position):
self.position = position
class SimpleExpr:
'''Simple example of an expression type handler'''
def __init__(self, name, expr, engine):
......
......@@ -87,7 +87,7 @@
Zope object encapsulating a Page Template.
"""
__version__='$Revision: 1.18 $'[11:-2]
__version__='$Revision: 1.19 $'[11:-2]
import os, AccessControl, Acquisition, sys
from Globals import DTMLFile, MessageDialog, package_home
......@@ -160,7 +160,7 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
security.declareProtected('View management screens',
'pt_editForm', 'manage_main', 'read',
'ZScriptHTML_tryForm', 'PrincipiaSearchSource',
'document_src')
'document_src', 'source.html', 'source.xml')
security.declareProtected('Change Page Templates',
'pt_editAction', 'pt_setTitle', 'pt_edit',
......@@ -310,6 +310,8 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
if RESPONSE is not None:
RESPONSE.setHeader('Content-Type', self.content_type)
if REQUEST.get('raw'):
return self._text
return self.read()
def __setstate__(self, state):
......
......@@ -2,9 +2,9 @@
<h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
tal:replace="structure here/manage_tabs">Tabs</h2>
<form action="" method="post"
tal:define="body request/other/text | request/form/text | here/read"
tal:attributes="action request/URL1">
<tal:block define="global body request/other/text | request/form/text
| here/read" />
<form action="" method="post" tal:attributes="action request/URL1">
<input type="hidden" name=":default_method" value="pt_changePrefs">
<table width="100%" cellspacing="0" cellpadding="2" border="0">
<tr>
......@@ -48,6 +48,24 @@
Expand macros when editing
</td>
</tr>
<tr tal:define="errors here/pt_errors" tal:condition="errors">
<tal:block define="global body python:here.document_src({'raw':1})"/>
<td align="left" valign="middle" class="form-label">Errors</td>
<td align="left" valign="middle" style="background-color: #FFDDDD"
colspan="3">
<pre tal:content="python:modules['string'].join(errors, '\n')">errors</pre>
</td>
</tr>
<tr tal:define="warnings here/pt_warnings" tal:condition="warnings">
<td align="left" valign="middle" class="form-label">Warnings</td>
<td align="left" valign="middle" style="background-color: #FFEEDD"
colspan="3">
<pre tal:content="python:modules['string'].join(warnings, '\n')">errors</pre>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="4">
<div style="width: 100%;">
......
......@@ -188,6 +188,9 @@ class HTMLTALParser(HTMLParser):
def getCode(self):
return self.gen.getCode()
def getWarnings(self):
return ()
# Overriding HTMLParser methods
def handle_starttag(self, tag, attrs):
......
......@@ -102,6 +102,7 @@ class TALGenerator:
from DummyEngine import DummyEngine
expressionCompiler = DummyEngine()
self.expressionCompiler = expressionCompiler
self.CompilerError = expressionCompiler.getCompilerError()
self.program = []
self.stack = []
self.todoStack = []
......@@ -229,7 +230,11 @@ class TALGenerator:
return self.todoStack.pop()
def compileExpression(self, expr):
return self.expressionCompiler.compile(expr)
try:
return self.expressionCompiler.compile(expr)
except self.CompilerError, err:
raise TALError('%s in expression %s' % (err.args[0], `expr`),
self.position)
def pushProgram(self):
self.stack.append(self.program)
......@@ -284,12 +289,13 @@ class TALGenerator:
def emitText(self, text):
self.emitRawText(cgi.escape(text))
def emitDefines(self, defines, position):
def emitDefines(self, defines):
for part in splitParts(defines):
m = re.match(
r"(?s)\s*(?:(global|local)\s+)?(%s)\s+(.*)\Z" % NAME_RE, part)
if not m:
raise TALError("invalid define syntax: " + `part`, position)
raise TALError("invalid define syntax: " + `part`,
self.position)
scope, name, expr = m.group(1, 2, 3)
scope = scope or "local"
cexpr = self.compileExpression(expr)
......@@ -298,9 +304,9 @@ class TALGenerator:
else:
self.emit("setGlobal", name, cexpr)
def emitOnError(self, name, onError, position):
def emitOnError(self, name, onError):
block = self.popProgram()
key, expr = parseSubstitution(onError, position)
key, expr = parseSubstitution(onError)
cexpr = self.compileExpression(expr)
if key == "text":
self.emit("insertText", cexpr, [])
......@@ -316,17 +322,18 @@ class TALGenerator:
program = self.popProgram()
self.emit("condition", cexpr, program)
def emitRepeat(self, arg, position=(None, None)):
def emitRepeat(self, arg):
m = re.match("(?s)\s*(%s)\s+(.*)\Z" % NAME_RE, arg)
if not m:
raise TALError("invalid repeat syntax: " + `arg`, position)
raise TALError("invalid repeat syntax: " + `arg`,
self.position)
name, expr = m.group(1, 2)
cexpr = self.compileExpression(expr)
program = self.popProgram()
self.emit("loop", name, cexpr, program)
def emitSubstitution(self, arg, attrDict={}, position=(None, None)):
key, expr = parseSubstitution(arg, position)
def emitSubstitution(self, arg, attrDict={}):
key, expr = parseSubstitution(arg)
cexpr = self.compileExpression(expr)
program = self.popProgram()
if key == "text":
......@@ -335,14 +342,15 @@ class TALGenerator:
assert key == "structure"
self.emit("insertStructure", cexpr, attrDict, program)
def emitDefineMacro(self, macroName, position=(None, None)):
def emitDefineMacro(self, macroName):
program = self.popProgram()
macroName = string.strip(macroName)
if self.macros.has_key(macroName):
raise METALError("duplicate macro definition: %s" % `macroName`,
position)
self.position)
if not re.match('%s$' % NAME_RE, macroName):
raise METALError("invalid macro name: %s" % `macroName`, position)
raise METALError("invalid macro name: %s" % `macroName`,
self.position)
self.macros[macroName] = program
self.inMacroDef = self.inMacroDef - 1
self.emit("defineMacro", macroName, program)
......@@ -353,21 +361,23 @@ class TALGenerator:
self.inMacroUse = 0
self.emit("useMacro", expr, cexpr, self.popSlots(), program)
def emitDefineSlot(self, slotName, position=(None, None)):
def emitDefineSlot(self, slotName):
program = self.popProgram()
slotName = string.strip(slotName)
if not re.match('%s$' % NAME_RE, slotName):
raise METALError("invalid slot name: %s" % `slotName`, position)
raise METALError("invalid slot name: %s" % `slotName`,
self.position)
self.emit("defineSlot", slotName, program)
def emitFillSlot(self, slotName, position=(None, None)):
def emitFillSlot(self, slotName):
program = self.popProgram()
slotName = string.strip(slotName)
if self.slots.has_key(slotName):
raise METALError("duplicate fill-slot name: %s" % `slotName`,
position)
self.position)
if not re.match('%s$' % NAME_RE, slotName):
raise METALError("invalid slot name: %s" % `slotName`, position)
raise METALError("invalid slot name: %s" % `slotName`,
self.position)
self.slots[slotName] = program
self.inMacroUse = 1
self.emit("fillSlot", slotName, program)
......@@ -443,6 +453,7 @@ class TALGenerator:
self.emitEndElement(name, isend)
return
self.position = position
for key, value in taldict.items():
if key not in KNOWN_TAL_ATTRIBUTES:
raise TALError("bad TAL attribute: " + `key`, position)
......@@ -527,7 +538,7 @@ class TALGenerator:
self.pushProgram() # block
todo["onError"] = onError
if define:
self.emitDefines(define, position)
self.emitDefines(define)
todo["define"] = define
if condition:
self.pushProgram()
......@@ -574,7 +585,7 @@ class TALGenerator:
self.emitEndTag(name)
return
position = todo.get("position", (None, None))
self.position = position = todo.get("position", (None, None))
defineMacro = todo.get("defineMacro")
useMacro = todo.get("useMacro")
defineSlot = todo.get("defineSlot")
......@@ -600,29 +611,29 @@ class TALGenerator:
(what, name, name), position)
if content:
self.emitSubstitution(content, {}, position)
self.emitSubstitution(content, {})
if optTag:
self.emitOptTag(name, optTag, isend)
elif not isend:
self.emitEndTag(name)
if replace:
self.emitSubstitution(replace, repldict, position)
self.emitSubstitution(replace, repldict)
if repeat:
self.emitRepeat(repeat, position)
self.emitRepeat(repeat)
if condition:
self.emitCondition(condition)
if onError:
self.emitOnError(name, onError, position)
self.emitOnError(name, onError)
if scope:
self.emit("endScope")
if defineSlot:
self.emitDefineSlot(defineSlot, position)
self.emitDefineSlot(defineSlot)
if fillSlot:
self.emitFillSlot(fillSlot, position)
self.emitFillSlot(fillSlot)
if useMacro:
self.emitUseMacro(useMacro)
if defineMacro:
self.emitDefineMacro(defineMacro, position)
self.emitDefineMacro(defineMacro)
def test():
t = TALGenerator()
......
......@@ -201,6 +201,7 @@ class TALInterpreter:
while self.scopeLevel > scopeLevel:
self.engine.endScope()
self.scopeLevel = self.scopeLevel - 1
self.engine.setPosition(self.position)
def restoreOutputState(self, state):
(dummy, self.col, self.stream, scopeLevel, level) = state
......@@ -279,6 +280,7 @@ class TALInterpreter:
def do_setPosition(self, position):
self.position = position
self.engine.setPosition(position)
bytecode_handlers["setPosition"] = do_setPosition
def do_startEndTag(self, stuff):
......
......@@ -107,6 +107,9 @@ class TALParser(XMLParser):
def getCode(self):
return self.gen.getCode()
def getWarnings(self):
return ()
def StartNamespaceDeclHandler(self, prefix, uri):
self.nsStack.append(self.nsDict.copy())
self.nsDict[uri] = prefix
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment