[ZPT] CVS: Packages/TAL - HTMLTALParser.py:1.23 TALDefs.py:1.13 TALGenerator.py:1.24 TALInterpreter.py:1.24

guido@digicool.com guido@digicool.com
Fri, 23 Mar 2001 16:35:49 -0500 (EST)


Update of /cvs-repository/Packages/TAL
In directory korak:/tmp/cvs-serv4958

Modified Files:
	HTMLTALParser.py TALDefs.py TALGenerator.py TALInterpreter.py 
Log Message:
- 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.




--- Updated File HTMLTALParser.py in package Packages/TAL --
--- HTMLTALParser.py	2001/03/22 17:35:43	1.22
+++ HTMLTALParser.py	2001/03/23 21:35:49	1.23
@@ -204,10 +204,10 @@
         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 @@
                 # 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()
 

--- Updated File TALDefs.py in package Packages/TAL --
--- TALDefs.py	2001/03/17 03:49:29	1.12
+++ TALDefs.py	2001/03/23 21:35:49	1.13
@@ -108,9 +108,11 @@
     "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 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 @@
         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"

--- Updated File TALGenerator.py in package Packages/TAL --
--- TALGenerator.py	2001/03/22 19:45:54	1.23
+++ TALGenerator.py	2001/03/23 21:35:49	1.24
@@ -221,6 +221,19 @@
             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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
             self.emitSubstitution(replace, repldict, position)
         if condition:
             self.emitCondition(condition)
+        if onError:
+            self.emitOnError(name, onError, position)
         if define:
             self.emit("endScope")
         if defineMacro:

--- Updated File TALInterpreter.py in package Packages/TAL --
--- TALInterpreter.py	2001/03/22 19:45:54	1.23
+++ TALInterpreter.py	2001/03/23 21:35:49	1.24
@@ -91,8 +91,13 @@
 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,7 +134,18 @@
         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:
             self.endsep = " />"
@@ -139,8 +155,6 @@
         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 @@
         else:
             self.col = len(s) - (i + 1)
 
-    level = 0
-
     def interpret(self, program):
         self.level = self.level + 1
         for item in program:
@@ -297,6 +309,21 @@
             self.interpret(compiledSlot)
         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