[ZPT] CVS: Zope/lib/python/TAL - DummyEngine.py:1.30 HTMLTALParser.py:1.32 TALDefs.py:1.27 TALGenerator.py:1.54 TALInterpreter.py:1.68 TALParser.py:1.18 XMLParser.py:1.8 __init__.py:1.2 driver.py:1.27 markbench.py:1.3 ndiff.py:1.3 runtest.py:1.21 setpath.py:1.4 timer.py:1.11

Shane Hathaway shane@cvs.zope.org
Wed, 3 Apr 2002 15:44:33 -0500


Update of /cvs-repository/Zope/lib/python/TAL
In directory cvs.zope.org:/tmp/cvs-serv3170/lib/python/TAL

Modified Files:
	DummyEngine.py HTMLTALParser.py TALDefs.py TALGenerator.py 
	TALInterpreter.py TALParser.py XMLParser.py __init__.py 
	driver.py markbench.py ndiff.py runtest.py setpath.py timer.py 
Log Message:
Merged shane-better-tracebacks-branch.  The changes are explained in http://dev.zope.org/Wikis/DevSite/Proposals/BetterTracebacks

=== Zope/lib/python/TAL/DummyEngine.py 1.29 => 1.30 ===
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -16,11 +17,10 @@
 
 import re
 import sys
-from string import rfind, strip
 
 import driver
 
-from TALDefs import NAME_RE, TALError, TALESError
+from TALDefs import NAME_RE, TALESError, ErrorInfo
 
 Default = []
 
@@ -32,6 +32,7 @@
 class DummyEngine:
 
     position = None
+    source_file = None
 
     def __init__(self, macros=None):
         if macros is None:
@@ -44,6 +45,9 @@
     def getCompilerError(self):
         return CompilerError
 
+    def setSourceFile(self, source_file):
+        self.source_file = source_file
+
     def setPosition(self, position):
         self.position = position
 
@@ -51,7 +55,8 @@
         return "$%s$" % expr
 
     def uncompile(self, expression):
-        assert expression[:1] == "$" == expression[-1:], expression
+        assert (expression.startswith("$") and expression.endswith("$"),
+            expression)
         return expression[1:-1]
 
     def beginScope(self):
@@ -71,7 +76,8 @@
         self.globals[name] = value
 
     def evaluate(self, expression):
-        assert expression[:1] == "$" == expression[-1:], expression
+        assert (expression.startswith("$") and expression.endswith("$"),
+            expression)
         expression = expression[1:-1]
         m = name_match(expression)
         if m:
@@ -82,7 +88,7 @@
         if type in ("string", "str"):
             return expr
         if type in ("path", "var", "global", "local"):
-            expr = strip(expr)
+            expr = expr.strip()
             if self.locals.has_key(expr):
                 return self.locals[expr]
             elif self.globals.has_key(expr):
@@ -97,8 +103,15 @@
             try:
                 return eval(expr, self.globals, self.locals)
             except:
-                raise TALESError("evaluation error in %s" % `expr`,
-                                 info=sys.exc_info())
+                raise TALESError("evaluation error in %s" % `expr`)
+        if type == "position":
+            # Insert the current source file name, line number,
+            # and column offset.
+            if self.position:
+                lineno, offset = self.position
+            else:
+                lineno, offset = None, None
+            return '%s (%s,%s)' % (self.source_file, lineno, offset)
         raise TALESError("unrecognized expression: " + `expression`)
 
     def evaluateValue(self, expr):
@@ -122,7 +135,8 @@
         return self.evaluate(expr)
 
     def evaluateMacro(self, macroName):
-        assert macroName[:1] == "$" == macroName[-1:], macroName
+        assert (macroName.startswith("$") and macroName.endswith("$"),
+            macroName)
         macroName = macroName[1:-1]
         file, localName = self.findMacroFile(macroName)
         if not file:
@@ -147,7 +161,7 @@
     def findMacroFile(self, macroName):
         if not macroName:
             raise TALESError("empty macro name")
-        i = rfind(macroName, '/')
+        i = macroName.rfind('/')
         if i < 0:
             # No slash -- must be a locally defined macro
             return None, macroName
@@ -161,8 +175,8 @@
         seq = self.evaluateSequence(expr)
         return Iterator(name, seq, self)
 
-    def getTALESError(self):
-        return TALESError
+    def createErrorInfo(self, err, position):
+        return ErrorInfo(err, position)
 
     def getDefault(self):
         return Default


=== Zope/lib/python/TAL/HTMLTALParser.py 1.31 => 1.32 ===
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -15,7 +16,6 @@
 """
 
 import sys
-import string
 
 from TALGenerator import TALGenerator
 from TALDefs import ZOPE_METAL_NS, ZOPE_TAL_NS, METALError, TALError
@@ -74,7 +74,7 @@
                        % (tagstack[0], endtag))
             else:
                 msg = ('Open tags <%s> do not match close tag </%s>'
-                       % (string.join(tagstack, '>, <'), endtag))
+                       % ('>, <'.join(tagstack), endtag))
         else:
             msg = 'No tags are open to match </%s>' % endtag
         HTMLParseError.__init__(self, msg, position)
@@ -235,7 +235,7 @@
     def scan_xmlns(self, attrs):
         nsnew = {}
         for key, value in attrs:
-            if key[:6] == "xmlns:":
+            if key.startswith("xmlns:"):
                 nsnew[key[6:]] = value
         if nsnew:
             self.nsstack.append(self.nsdict)
@@ -249,7 +249,7 @@
 
     def fixname(self, name):
         if ':' in name:
-            prefix, suffix = string.split(name, ':', 1)
+            prefix, suffix = name.split(':', 1)
             if prefix == 'xmlns':
                 nsuri = self.nsdict.get(suffix)
                 if nsuri in (ZOPE_TAL_NS, ZOPE_METAL_NS):


=== Zope/lib/python/TAL/TALDefs.py 1.26 => 1.27 ===
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -66,27 +67,22 @@
     pass
 
 class TALESError(TALError):
+    pass
+
+
+class ErrorInfo:
+
+    def __init__(self, err, position=(None, None)):
+        if isinstance(err, Exception):
+            self.type = err.__class__
+            self.value = err
+        else:
+            self.type = err
+            self.value = None
+        self.lineno = position[0]
+        self.offset = position[1]
 
-    # This exception can carry around another exception + traceback
 
-    def takeTraceback(self):
-        t = self.info[2]
-        self.info = self.info[:2] + (None,)
-        return t
-
-    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+([^\s].*)\Z", re.S)
@@ -117,11 +113,10 @@
 def splitParts(arg):
     # Break in pieces at undoubled semicolons and
     # change double semicolons to singles:
-    import string
-    arg = string.replace(arg, ";;", "\0")
-    parts = string.split(arg, ';')
-    parts = map(lambda s, repl=string.replace: repl(s, "\0", ";"), parts)
-    if len(parts) > 1 and not string.strip(parts[-1]):
+    arg = arg.replace(";;", "\0")
+    parts = arg.split(';')
+    parts = [p.replace("\0", ";") for p in parts]
+    if len(parts) > 1 and not parts[-1].strip():
         del parts[-1] # It ended in a semicolon
     return parts
 
@@ -139,7 +134,7 @@
     return None
 
 def getProgramVersion(program):
-    if (isinstance(program, ListType) and len(program) >= 2 and
+    if (len(program) >= 2 and
         isinstance(program[0], TupleType) and len(program[0]) == 2):
         opcode, version = program[0]
         if opcode == "version":


=== Zope/lib/python/TAL/TALGenerator.py 1.53 => 1.54 ===
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -14,7 +15,6 @@
 Code generator for TALInterpreter intermediate code.
 """
 
-import string
 import re
 import cgi
 
@@ -24,8 +24,9 @@
 
     inMacroUse = 0
     inMacroDef = 0
+    source_file = None
     
-    def __init__(self, expressionCompiler=None, xml=1):
+    def __init__(self, expressionCompiler=None, xml=1, source_file=None):
         if not expressionCompiler:
             from DummyEngine import DummyEngine
             expressionCompiler = DummyEngine()
@@ -40,6 +41,9 @@
         self.xml = xml
         self.emit("version", TAL_VERSION)
         self.emit("mode", xml and "xml" or "html")
+        if source_file is not None:
+            self.source_file = source_file
+            self.emit("setSourceFile", source_file)
 
     def getCode(self):
         assert not self.stack
@@ -78,9 +82,9 @@
                 # instructions to be joined together.
                 output.append(self.optimizeArgsList(item))
                 continue
-            text = string.join(collect, "")
+            text = "".join(collect)
             if text:
-                i = string.rfind(text, "\n")
+                i = text.rfind("\n")
                 if i >= 0:
                     i = len(text) - (i + 1)
                     output.append(("rawtextColumn", (text, i)))
@@ -272,7 +276,7 @@
 
     def emitDefineMacro(self, macroName):
         program = self.popProgram()
-        macroName = string.strip(macroName)
+        macroName = macroName.strip()
         if self.macros.has_key(macroName):
             raise METALError("duplicate macro definition: %s" % `macroName`,
                              self.position)
@@ -291,7 +295,7 @@
 
     def emitDefineSlot(self, slotName):
         program = self.popProgram()
-        slotName = string.strip(slotName)
+        slotName = slotName.strip()
         if not re.match('%s$' % NAME_RE, slotName):
             raise METALError("invalid slot name: %s" % `slotName`,
                              self.position)
@@ -299,7 +303,7 @@
 
     def emitFillSlot(self, slotName):
         program = self.popProgram()
-        slotName = string.strip(slotName)
+        slotName = slotName.strip()
         if self.slots.has_key(slotName):
             raise METALError("duplicate fill-slot name: %s" % `slotName`,
                              self.position)
@@ -330,7 +334,7 @@
                 self.program[i] = ("rawtext", text[:m.start()])
                 collect.append(m.group())
         collect.reverse()
-        return string.join(collect, "")
+        return "".join(collect)
 
     def unEmitNewlineWhitespace(self):
         collect = []
@@ -349,7 +353,7 @@
                 break
             text, rest = m.group(1, 2)
             collect.reverse()
-            rest = rest + string.join(collect, "")
+            rest = rest + "".join(collect)
             del self.program[i:]
             if text:
                 self.emit("rawtext", text)
@@ -427,6 +431,8 @@
         if self.inMacroUse:
             if fillSlot:
                 self.pushProgram()
+                if self.source_file is not None:
+                    self.emit("setSourceFile", self.source_file)
                 todo["fillSlot"] = fillSlot
                 self.inMacroUse = 0
         else:
@@ -438,6 +444,8 @@
                 self.pushProgram()
                 self.emit("version", TAL_VERSION)
                 self.emit("mode", self.xml and "xml" or "html")
+                if self.source_file is not None:
+                    self.emit("setSourceFile", self.source_file)
                 todo["defineMacro"] = defineMacro
                 self.inMacroDef = self.inMacroDef + 1
             if useMacro:


=== Zope/lib/python/TAL/TALInterpreter.py 1.67 => 1.68 ===
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -85,7 +86,6 @@
         self.program = program
         self.macros = macros
         self.engine = engine
-        self.TALESError = engine.getTALESError()
         self.Default = engine.getDefault()
         self.stream = stream or sys.stdout
         self._stream_write = self.stream.write
@@ -112,6 +112,7 @@
         self.col = 0
         self.level = 0
         self.scopeLevel = 0
+        self.sourceFile = None
 
     def saveState(self):
         return (self.position, self.col, self.stream,
@@ -201,6 +202,11 @@
         self.endlen = len(self.endsep)
     bytecode_handlers["mode"] = do_mode
 
+    def do_setSourceFile(self, source_file):
+        self.sourceFile = source_file
+        self.engine.setSourceFile(source_file)
+    bytecode_handlers["setSourceFile"] = do_setSourceFile
+
     def do_setPosition(self, position):
         self.position = position
         self.engine.setPosition(position)
@@ -515,7 +521,12 @@
                 raise METALError("macro %s has incompatible mode %s" %
                                  (`macroName`, `mode`), self.position)
         self.pushMacro(macroName, compiledSlots)
+        saved_source = self.sourceFile
+        saved_position = self.position  # Used by Boa Constructor
         self.interpret(macro)
+        if self.sourceFile != saved_source:
+            self.engine.setSourceFile(saved_source)
+            self.sourceFile = saved_source
         self.popMacro()
     bytecode_handlers["useMacro"] = do_useMacro
 
@@ -531,10 +542,15 @@
             return
         macs = self.macroStack
         if macs and macs[-1] is not None:
+            saved_source = self.sourceFile
+            saved_position = self.position  # Used by Boa Constructor
             macroName, slots = self.popMacro()[:2]
             slot = slots.get(slotName)
             if slot is not None:
                 self.interpret(slot)
+                if self.sourceFile != saved_source:
+                    self.engine.setSourceFile(saved_source)
+                    self.sourceFile = saved_source
                 self.pushMacro(macroName, slots, entering=0)
                 return
             self.pushMacro(macroName, slots)
@@ -553,16 +569,16 @@
         self._stream_write = stream.write
         try:
             self.interpret(block)
-        except self.TALESError, err:
+        except:
+            exc = sys.exc_info()[1]
             self.restoreState(state)
             engine = self.engine
             engine.beginScope()
-            err.lineno, err.offset = self.position
-            engine.setLocal('error', err)
+            error = engine.createErrorInfo(exc, self.position)
+            engine.setLocal('error', error)
             try:
                 self.interpret(handler)
             finally:
-                err.takeTraceback()
                 engine.endScope()
         else:
             self.restoreOutputState(state)


=== Zope/lib/python/TAL/TALParser.py 1.17 => 1.18 ===
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -14,7 +15,6 @@
 Parse XML and compile to TALInterpreter intermediate code.
 """
 
-import string
 from XMLParser import XMLParser
 from TALDefs import *
 from TALGenerator import TALGenerator
@@ -99,7 +99,7 @@
 
     def fixname(self, name):
         if ' ' in name:
-            uri, name = string.split(name, ' ')
+            uri, name = name.split(' ')
             prefix = self.nsDict[uri]
             prefixed = name
             if prefix:


=== Zope/lib/python/TAL/XMLParser.py 1.7 => 1.8 ===
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.


=== Zope/lib/python/TAL/__init__.py 1.1 => 1.2 ===
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+# 
+##############################################################################
+""" Template Attribute Language package """


=== Zope/lib/python/TAL/driver.py 1.26 => 1.27 ===
+#!/usr/bin/env python
 ##############################################################################
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -17,7 +18,6 @@
 
 import os
 import sys
-import string
 
 import getopt
 
@@ -93,7 +93,7 @@
     assert mode in ("html", "xml", None)
     if mode is None:
         ext = os.path.splitext(file)[1]
-        if string.lower(ext) in (".html", ".htm"):
+        if ext.lower() in (".html", ".htm"):
             mode = "html"
         else:
             mode = "xml"


=== Zope/lib/python/TAL/markbench.py 1.2 => 1.3 ===
+#! /usr/bin/env python
+
+# This software is subject to the provisions of the Zope Public License,
+# Version 1.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 
+# FOR A PARTICULAR PURPOSE.
+
 
 '''Run benchmarks of TAL vs. DTML'''
 


=== Zope/lib/python/TAL/ndiff.py 1.2 => 1.3 ===
 # have been in sys.argv[1:] had the cmd-line form been used.
 
-import string
 TRACE = 0
 
 # define what "junk" means


=== Zope/lib/python/TAL/runtest.py 1.20 => 1.21 ===
+#! /usr/bin/env python
 ##############################################################################
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -17,7 +18,6 @@
 
 import sys
 import os
-import string
 from cStringIO import StringIO
 import glob
 import traceback
@@ -57,7 +57,7 @@
     if args and args[0] == "-Q":
         unittesting = 1
         del args[0]
-    while args and args[0][:1] == '-':
+    while args and args[0].startswith('-'):
         opts.append(args[0])
         del args[0]
     if not args:
@@ -76,12 +76,12 @@
     errors = 0
     for arg in args:
         locopts = []
-        if string.find(arg, "metal") >= 0 and "-m" not in opts:
+        if arg.find("metal") >= 0 and "-m" not in opts:
             locopts.append("-m")
         if not unittesting:
             print arg,
             sys.stdout.flush()
-        if tests.utils.skipxml and arg[-4:] == ".xml":
+        if tests.utils.skipxml and arg.endswith(".xml"):
             print "SKIPPED (XML parser not available)"
             continue
         save = sys.stdout, sys.argv
@@ -109,7 +109,7 @@
             continue
         head, tail = os.path.split(arg)
         outfile = os.path.join(
-            string.replace(head, "input", "output"),
+            head.replace("input", "output"),
             tail)
         try:
             f = open(outfile)


=== Zope/lib/python/TAL/setpath.py 1.3 => 1.4 ===
+# Version 1.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 
+# FOR A PARTICULAR PURPOSE.
+
 """
 Read a module search path from .path.
 """
 import os
 import sys
-import string
 
 dir = os.path.dirname(__file__)
 path = os.path.join(dir, ".path")
@@ -13,7 +19,7 @@
     raise IOError, "Please edit .path to point to <Zope2/lib/python>"
 else:
     for line in f.readlines():
-        line = string.strip(line)
+        line = line.strip()
         if line and line[0] != '#':
             for dir in string.split(line, os.pathsep):
                 dir = os.path.expanduser(os.path.expandvars(dir))


=== Zope/lib/python/TAL/timer.py 1.10 => 1.11 ===
+#! /usr/bin/env python
 ##############################################################################
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
 # 
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.