Commit 2f4018b1 authored by Guido van Rossum's avatar Guido van Rossum

- Support on-error. The engine must raise TALDefs.TALESError() to

  trigger it (other errors are not caught).  TALESError() takes up to
  three args: msg, position, info, where msg is the message string,
  position is a (lineno, offset) tuple, and info is the exception info
  tuple (type, value, traceback) from sys.exc_info().  Both default to
  tuples with all None values.  You can choose to subclass this
  exception of course.

- Require explicit </endtag> for <starttags> that have TAL or METAL
  attributes.  (Note that this detected a bug in the first example on
  http://dev.zope.org/Wikis/DevSite/Projects/ZPT/RenderErrorHandlingStrategies;
  the <span> wasn't closed.
parent c0c5b904
......@@ -204,10 +204,10 @@ class HTMLTALParser(HTMLParser):
self.close_para_tags(tag)
self.scan_xmlns(attrs)
attrlist, taldict, metaldict = self.extract_attrs(attrs)
if taldict.get("replace") or taldict.get("content"):
if taldict.get("content"):
self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
self.getpos())
self.gen.emitEndElement(tag)
self.gen.emitEndElement(tag, implied=-1)
else:
self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
self.getpos(), isend=1)
......@@ -261,11 +261,11 @@ class HTMLTALParser(HTMLParser):
# Pick out trailing whitespace from the program, and
# insert the close tag before the whitespace.
white = self.gen.unEmitWhitespace()
self.gen.emitEndElement(tag)
self.gen.emitEndElement(tag, implied=implied)
if white:
self.gen.emitRawText(white)
else:
self.gen.emitEndElement(tag)
self.gen.emitEndElement(tag, implied=implied)
self.tagstack.pop()
self.pop_xmlns()
......
......@@ -108,9 +108,11 @@ KNOWN_TAL_ATTRIBUTES = [
"replace",
"repeat",
"attributes",
"on-error",
]
class TALError(Exception):
def __init__(self, msg, position=(None, None)):
assert msg != ""
self.msg = msg
......@@ -128,6 +130,24 @@ class TALError(Exception):
class METALError(TALError):
pass
class TALESError(TALError):
# This exception can carry around another exception + traceback
def __init__(self, msg, position=(None, None), info=(None, None, None)):
t, v, tb = info
if t:
if issubclass(t, Exception) and t.__module__ == "exceptions":
err = t.__name__
else:
err = str(t)
v = v is not None and str(v)
if v:
err = "%s: %s" % (err, v)
msg = "%s: %s" % (msg, err)
TALError.__init__(self, msg, position)
self.info = info
import re
_attr_re = re.compile(r"\s*([^\s]+)\s*(.*)\Z", re.S)
_subst_re = re.compile(r"\s*(?:(text|structure)\s+)?(.*)\Z", re.S)
......@@ -147,11 +167,11 @@ def parseAttributeReplacements(arg):
dict[name] = expr
return dict
def parseSubstitution(arg):
def parseSubstitution(arg, position=(None, None)):
m = _subst_re.match(arg)
if not m:
print "Bad syntax in replace/content:", `arg`
return None, None
raise TALError("Bad syntax in substitution text: " + `onError`,
position)
key, expr = m.group(1, 2)
if not key:
key = "text"
......
......@@ -221,6 +221,19 @@ class TALGenerator:
else:
self.emit("setGlobal", name, cexpr)
def emitOnError(self, name, onError, position):
block = self.popProgram()
key, expr = parseSubstitution(onError, position)
cexpr = self.compileExpression(expr)
if key == "text":
self.emit("insertText", cexpr, [])
else:
assert key == "structure"
self.emit("insertStructure", cexpr, attrDict, [])
self.emitEndTag(name)
handler = self.popProgram()
self.emit("onError", block, handler)
def emitCondition(self, expr):
cexpr = self.compileExpression(expr)
program = self.popProgram()
......@@ -236,9 +249,7 @@ class TALGenerator:
self.emit("loop", name, cexpr, program)
def emitSubstitution(self, arg, attrDict={}, position=(None, None)):
key, expr = parseSubstitution(arg)
if not key:
raise TALError("Bad syntax in content/replace: " + `arg`, position)
key, expr = parseSubstitution(arg, position)
cexpr = self.compileExpression(expr)
program = self.popProgram()
if key == "text":
......@@ -346,12 +357,13 @@ class TALGenerator:
useMacro = metaldict.get("use-macro")
defineSlot = metaldict.get("define-slot")
fillSlot = metaldict.get("fill-slot")
defines = taldict.get("define")
define = taldict.get("define")
condition = taldict.get("condition")
content = taldict.get("content")
replace = taldict.get("replace")
repeat = taldict.get("repeat")
attrsubst = taldict.get("attributes")
onError = taldict.get("on-error")
if len(metaldict) > 1:
raise METALError("at most one METAL attribute per element",
position)
......@@ -383,10 +395,16 @@ class TALGenerator:
if fillSlot:
self.pushProgram()
todo["fillSlot"] = fillSlot
if defines:
if define:
self.emit("beginScope")
self.emitDefines(defines, position)
todo["define"] = defines
if onError:
self.pushProgram() # handler
self.emitStartTag(name, attrlist)
self.pushProgram() # block
todo["onError"] = onError
if define:
self.emitDefines(define, position)
todo["define"] = define
if condition:
self.pushProgram()
todo["condition"] = condition
......@@ -417,7 +435,7 @@ class TALGenerator:
if isend:
self.emitEndElement(name, isend)
def emitEndElement(self, name, isend=0):
def emitEndElement(self, name, isend=0, implied=0):
todo = self.todoPop()
if not todo:
# Shortcut
......@@ -434,9 +452,20 @@ class TALGenerator:
repeat = todo.get("repeat")
replace = todo.get("replace")
condition = todo.get("condition")
onError = todo.get("onError")
define = todo.get("define")
repldict = todo.get("repldict", {})
if implied > 0:
if defineMacro or useMacro or defineSlot or fillSlot:
exc = METALError
what = "METAL"
else:
exc = TALError
what = "TAL"
raise exc("%s attributes on <%s> require explicit </%s>" %
(what, name, name), position)
if content:
self.emitSubstitution(content, {}, position)
if not isend:
......@@ -448,6 +477,8 @@ class TALGenerator:
self.emitSubstitution(replace, repldict, position)
if condition:
self.emitCondition(condition)
if onError:
self.emitOnError(name, onError, position)
if define:
self.emit("endScope")
if defineMacro:
......
......@@ -91,8 +91,13 @@ import string
import getopt
import cgi
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from XMLParser import XMLParser
from TALDefs import TALError, quote
from TALDefs import TALError, TALESError, quote
BOOLEAN_HTML_ATTRS = [
# List of Boolean attributes in HTML that should be rendered in
......@@ -129,6 +134,17 @@ class TALInterpreter:
self.slots = {}
self.currentMacro = None
self.position = None, None # (lineno, offset)
self.col = 0
self.level = 0
def saveState(self):
return (self.position, self.col, self.stream)
def restoreState(self, state):
(self.position, self.col, self.stream) = state
def restoreOutputState(self, state):
(dummy, self.col, self.stream) = state
def __call__(self):
if self.html:
......@@ -139,8 +155,6 @@ class TALInterpreter:
if self.col > 0:
self.stream_write("\n")
col = 0
def stream_write(self, s):
self.stream.write(s)
i = string.rfind(s, '\n')
......@@ -149,8 +163,6 @@ class TALInterpreter:
else:
self.col = len(s) - (i + 1)
level = 0
def interpret(self, program):
self.level = self.level + 1
for item in program:
......@@ -298,6 +310,21 @@ class TALInterpreter:
else:
self.interpret(block)
def do_onError(self, block, handler):
if not self.tal:
self.interpret(block)
return
state = self.saveState()
self.stream = stream = StringIO()
try:
self.interpret(block)
except TALESError, err:
self.restoreState(state)
self.interpret(handler)
else:
self.restoreOutputState(state)
self.stream_write(stream.getvalue())
def test():
from driver import FILE, parsefile
from DummyEngine import DummyEngine
......
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