[ZPT] CVS: Zope27/lib/python/TAL - TALInterpreter.py:1.68.26.3

Fred L. Drake, Jr. fdrake@acm.org
Tue, 17 Sep 2002 12:30:48 -0400


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

Modified Files:
      Tag: Zope-2_7-development-branch
	TALInterpreter.py 
Log Message:
FasterStringIO:  Faster, append-only subclass of the StringIO, since
    we were spending a lot of time in the write() method.

interpret():  Remove the tmpstream argument.

interpretWithStream():  New method; equivalent to interpret(prog, stream).

Some additional minor optimizations.


=== Zope27/lib/python/TAL/TALInterpreter.py 1.68.26.2 => 1.68.26.3 ===
--- Zope27/lib/python/TAL/TALInterpreter.py:1.68.26.2	Sun Sep  8 22:04:52 2002
+++ Zope27/lib/python/TAL/TALInterpreter.py	Tue Sep 17 12:30:47 2002
@@ -18,7 +18,7 @@
 import sys
 import getopt
 import re
-from types import ListType
+from types import ListType, StringTypes
 from cgi import escape
 # Do not use cStringIO here!  It's not unicode aware. :(
 from StringIO import StringIO
@@ -119,7 +119,7 @@
     def StringIO(self):
         # Third-party products wishing to provide a full Unicode-aware
         # StringIO can do so by monkey-patching this method.
-        return StringIO()
+        return FasterStringIO()
 
     def saveState(self):
         return (self.position, self.col, self.stream,
@@ -184,15 +184,20 @@
 
     bytecode_handlers = {}
 
-    def interpret(self, program, tmpstream=None):
+    def interpretWithStream(self, program, stream):
+        oldstream = self.stream
+        self.stream = stream
+        self._stream_write = stream.write
+        try:
+            self.interpret(program)
+        finally:
+            self.stream = oldstream
+            self._stream_write = oldstream.write
+
+    def interpret(self, program):
         oldlevel = self.level
         self.level = oldlevel + 1
         handlers = self.dispatch
-        if tmpstream:
-            ostream = self.stream
-            owrite = self._stream_write
-            self.stream = tmpstream
-            self._stream_write = tmpstream.write
         try:
             if self.debug:
                 for (opcode, args) in program:
@@ -207,9 +212,6 @@
                     handlers[opcode](self, args)
         finally:
             self.level = oldlevel
-            if tmpstream:
-                self.stream = ostream
-                self._stream_write = owrite
 
     def do_version(self, version):
         assert version == TAL_VERSION
@@ -245,8 +247,8 @@
         # for start tags with no attributes; those are optimized down
         # to rawtext events.  Hence, there is no special "fast path"
         # for that case.
-        _stream_write = self._stream_write
-        _stream_write("<" + name)
+        L = ["<", name]
+        append = L.append
         col = self.col + _len(name) + 1
         wrap = self.wrap
         align = col + 1
@@ -265,13 +267,14 @@
                 if (wrap and
                     col >= align and
                     col + 1 + slen > wrap):
-                    _stream_write("\n" + " "*align)
+                    append("\n" + " "*align)
                     col = align + slen
                 else:
-                    s = " " + s
+                    append(" ")
                     col = col + 1 + slen
-                _stream_write(s)
-            _stream_write(end)
+                append(s)
+            append(end)
+            self._stream_write("".join(L))
             col = col + endlen
         finally:
             self.col = col
@@ -362,7 +365,7 @@
                   omit=0):
         if tag_ns and not self.showtal:
             return self.no_tag(start, program)
-            
+
         self.interpret(start)
         if not isend:
             self.interpret(program)
@@ -390,7 +393,8 @@
     def do_rawtextBeginScope(self, (s, col, position, closeprev, dict)):
         self._stream_write(s)
         self.col = col
-        self.do_setPosition(position)
+        self.position = position
+        self.engine.setPosition(position)
         if closeprev:
             engine = self.engine
             engine.endScope()
@@ -402,7 +406,8 @@
     def do_rawtextBeginScope_tal(self, (s, col, position, closeprev, dict)):
         self._stream_write(s)
         self.col = col
-        self.do_setPosition(position)
+        self.position = position
+        self.engine.setPosition(position)
         engine = self.engine
         if closeprev:
             engine.endScope()
@@ -480,7 +485,7 @@
             state = self.saveState()
             try:
                 tmpstream = self.StringIO()
-                self.interpret(program, tmpstream)
+                self.interpretWithStream(program, tmpstream)
                 value = normalize(tmpstream.getvalue())
             finally:
                 self.restoreState(state)
@@ -515,7 +520,7 @@
         # Use a temporary stream to capture the interpretation of the
         # subnodes, which should /not/ go to the output stream.
         tmpstream = self.StringIO()
-        self.interpret(stuff[1], tmpstream)
+        self.interpretWithStream(stuff[1], tmpstream)
         # We only care about the evaluated contents if we need an implicit
         # message id.  All other useful information will be in the i18ndict on
         # the top of the i18nStack.
@@ -723,6 +728,30 @@
     bytecode_handlers_tal["onError"] = do_onError_tal
     bytecode_handlers_tal["<attrAction>"] = attrAction_tal
     bytecode_handlers_tal["optTag"] = do_optTag_tal
+
+
+class FasterStringIO(StringIO):
+    """Append-only version of StringIO.
+
+    This let's us have a much faster write() method.
+    """
+    def close(self):
+        if not self.closed:
+            self.write = _write_ValueError
+            StringIO.close(self)
+
+    def seek(self, pos, mode=0):
+        raise RuntimeError("FasterStringIO.seek() not allowed")
+
+    def write(self, s):
+        #assert self.pos == self.len
+        newpos = self.pos + len(s)
+        self.buflist.append(s)
+        self.len = self.pos = newpos
+
+
+def _write_ValueError(s):
+    raise ValueError, "I/O operation on closed file"
 
 
 def test():