Commit 3a24de4f authored by Florent Guillaume's avatar Florent Guillaume

Merged Fred's latest TAL/ZPT optimizations from Zope27 into HEAD.

The merge point in Zope27 will be tagged mergepoint-to-Zope26.
parent 52193d9b
...@@ -17,7 +17,7 @@ Page Template-specific implementation of TALES, with handlers ...@@ -17,7 +17,7 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths. for Python expressions, string literals, and paths.
""" """
__version__='$Revision: 1.39 $'[11:-2] __version__='$Revision: 1.40 $'[11:-2]
import re, sys import re, sys
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \ from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
...@@ -46,6 +46,8 @@ def installHandlers(engine): ...@@ -46,6 +46,8 @@ def installHandlers(engine):
if sys.modules.has_key('Zope'): if sys.modules.has_key('Zope'):
import AccessControl import AccessControl
import AccessControl.cAccessControl
acquisition_security_filter = AccessControl.cAccessControl.aq_validate
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from AccessControl.ZopeGuards import guarded_getattr from AccessControl.ZopeGuards import guarded_getattr
try: try:
...@@ -65,6 +67,12 @@ else: ...@@ -65,6 +67,12 @@ else:
from zExceptions import Unauthorized from zExceptions import Unauthorized
except ImportError: except ImportError:
Unauthorized = "Unauthorized" Unauthorized = "Unauthorized"
def acquisition_security_filter(orig, inst, name, v, real_validate):
if real_validate(orig, inst, name, v):
return 1
raise Unauthorized, name
def call_with_ns(f, ns, arg=1): def call_with_ns(f, ns, arg=1):
if arg==2: if arg==2:
return f(None, ns) return f(None, ns)
...@@ -279,28 +287,27 @@ class DeferExpr: ...@@ -279,28 +287,27 @@ class DeferExpr:
return 'defer:%s' % `self._s` return 'defer:%s' % `self._s`
def restrictedTraverse(self, path, securityManager, def restrictedTraverse(object, path, securityManager,
get=getattr, has=hasattr, N=None, M=[], get=getattr, has=hasattr, N=None, M=[],
TupleType=type(()) ): TupleType=type(()) ):
REQUEST = {'path': path}
REQUEST['TraversalRequestNameStack'] = path = path[:] # Copy!
if not path[0]: if not path[0]:
# If the path starts with an empty string, go to the root first. # If the path starts with an empty string, go to the root first.
self = self.getPhysicalRoot() object = object.getPhysicalRoot()
if not securityManager.validateValue(self): if not securityManager.validateValue(object):
raise Unauthorized, name raise Unauthorized, name
path.pop(0) path.pop(0)
REQUEST = {'path': path}
REQUEST['TraversalRequestNameStack'] = path = path[:] # Copy!
path.reverse() path.reverse()
validate = securityManager.validate validate = securityManager.validate
object = self
while path:
__traceback_info__ = REQUEST __traceback_info__ = REQUEST
while path:
name = path.pop() name = path.pop()
if isinstance(name, TupleType): if isinstance(name, TupleType):
object = apply(object, name) object = object(*name)
continue continue
if name[0] == '_': if name[0] == '_':
...@@ -363,9 +370,3 @@ def restrictedTraverse(self, path, securityManager, ...@@ -363,9 +370,3 @@ def restrictedTraverse(self, path, securityManager,
object = o object = o
return object return object
def validate2(orig, inst, name, v, real_validate):
if not real_validate(orig, inst, name, v):
raise Unauthorized, name
return 1
...@@ -15,17 +15,16 @@ ...@@ -15,17 +15,16 @@
HTML- and XML-based template objects using TAL, TALES, and METAL. HTML- and XML-based template objects using TAL, TALES, and METAL.
""" """
__version__='$Revision: 1.28 $'[11:-2] __version__='$Revision: 1.29 $'[11:-2]
import sys import sys
from TAL.TALParser import TALParser from TAL.TALParser import TALParser
from TAL.HTMLTALParser import HTMLTALParser from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALGenerator import TALGenerator from TAL.TALGenerator import TALGenerator
from TAL.TALInterpreter import TALInterpreter
from Expressions import getEngine
# Do not use cStringIO here! It's not unicode aware. :( # Do not use cStringIO here! It's not unicode aware. :(
from StringIO import StringIO from TAL.TALInterpreter import TALInterpreter, FasterStringIO
from Expressions import getEngine
from ExtensionClass import Base from ExtensionClass import Base
from ComputedAttribute import ComputedAttribute from ComputedAttribute import ComputedAttribute
...@@ -47,7 +46,7 @@ class PageTemplate(Base): ...@@ -47,7 +46,7 @@ class PageTemplate(Base):
def StringIO(self): def StringIO(self):
# Third-party products wishing to provide a full Unicode-aware # Third-party products wishing to provide a full Unicode-aware
# StringIO can do so by monkey-patching this method. # StringIO can do so by monkey-patching this method.
return StringIO() return FasterStringIO()
def macros(self): def macros(self):
return self.pt_macros() return self.pt_macros()
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
"""Generic Python Expression Handler """Generic Python Expression Handler
""" """
__version__='$Revision: 1.9 $'[11:-2] __version__='$Revision: 1.10 $'[11:-2]
from TALES import CompilerError from TALES import CompilerError
from sys import exc_info from sys import exc_info
...@@ -43,18 +43,19 @@ class PythonExpr: ...@@ -43,18 +43,19 @@ class PythonExpr:
if vname[0] not in '$_': if vname[0] not in '$_':
vnames.append(vname) vnames.append(vname)
def _bind_used_names(self, econtext): def _bind_used_names(self, econtext, _marker=[]):
# Bind template variables # Bind template variables
names = {} names = {}
vars = econtext.vars vars = econtext.vars
getType = econtext._engine.getTypes().get getType = econtext._engine.getTypes().get
for vname in self._f_varnames: for vname in self._f_varnames:
has, val = vars.has_get(vname) val = vars.get(vname, _marker)
if not has: if val is _marker:
has = val = getType(vname) has = val = getType(vname)
if has: if has:
val = ExprTypeProxy(vname, val, econtext) val = ExprTypeProxy(vname, val, econtext)
if has: names[vname] = val
else:
names[vname] = val names[vname] = val
return names return names
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
An implementation of a generic TALES engine An implementation of a generic TALES engine
""" """
__version__='$Revision: 1.32 $'[11:-2] __version__='$Revision: 1.33 $'[11:-2]
import re, sys, ZTUtils import re, sys, ZTUtils
from MultiMapping import MultiMapping from MultiMapping import MultiMapping
...@@ -44,8 +44,6 @@ class Default: ...@@ -44,8 +44,6 @@ class Default:
'''Retain Default''' '''Retain Default'''
Default = Default() Default = Default()
_marker = []
class SafeMapping(MultiMapping): class SafeMapping(MultiMapping):
'''Mapping with security declarations and limited method exposure. '''Mapping with security declarations and limited method exposure.
...@@ -60,9 +58,6 @@ class SafeMapping(MultiMapping): ...@@ -60,9 +58,6 @@ class SafeMapping(MultiMapping):
_push = MultiMapping.push _push = MultiMapping.push
_pop = MultiMapping.pop _pop = MultiMapping.pop
def has_get(self, key, _marker=[]):
v = self.get(key, _marker)
return v is not _marker, v
class Iterator(ZTUtils.Iterator): class Iterator(ZTUtils.Iterator):
def __init__(self, name, seq, context): def __init__(self, name, seq, context):
...@@ -216,8 +211,7 @@ class Context: ...@@ -216,8 +211,7 @@ class Context:
expression = self._engine.compile(expression) expression = self._engine.compile(expression)
__traceback_supplement__ = ( __traceback_supplement__ = (
TALESTracebackSupplement, self, expression) TALESTracebackSupplement, self, expression)
v = expression(self) return expression(self)
return v
evaluateValue = evaluate evaluateValue = evaluate
...@@ -257,7 +251,6 @@ class Context: ...@@ -257,7 +251,6 @@ class Context:
domain, msgid, mapping=mapping, domain, msgid, mapping=mapping,
context=context, target_language=target_language) context=context, target_language=target_language)
class TALESTracebackSupplement: class TALESTracebackSupplement:
"""Implementation of ITracebackSupplement""" """Implementation of ITracebackSupplement"""
def __init__(self, context, expression): def __init__(self, context, expression):
...@@ -276,8 +269,6 @@ class TALESTracebackSupplement: ...@@ -276,8 +269,6 @@ class TALESTracebackSupplement:
else: else:
from cgi import escape from cgi import escape
return '<b>Names:</b><pre>%s</pre>' % (escape(s)) return '<b>Names:</b><pre>%s</pre>' % (escape(s))
return None
class SimpleExpr: class SimpleExpr:
...@@ -289,4 +280,3 @@ class SimpleExpr: ...@@ -289,4 +280,3 @@ class SimpleExpr:
return self._name, self._expr return self._name, self._expr
def __repr__(self): def __repr__(self):
return '<SimpleExpr %s %s>' % (self._name, `self._expr`) return '<SimpleExpr %s %s>' % (self._name, `self._expr`)
...@@ -163,8 +163,3 @@ def getProgramVersion(program): ...@@ -163,8 +163,3 @@ def getProgramVersion(program):
if opcode == "version": if opcode == "version":
return version return version
return None return None
import cgi
def quote(s, escape=cgi.escape):
return '"%s"' % escape(s, 1)
del cgi
...@@ -162,7 +162,7 @@ class TALGenerator: ...@@ -162,7 +162,7 @@ class TALGenerator:
if item[1] is None: if item[1] is None:
s = item[0] s = item[0]
else: else:
s = "%s=%s" % (item[0], TALDefs.quote(item[1])) s = '%s="%s"' % (item[0], cgi.escape(item[1], 1))
attrlist[i] = item[0], s attrlist[i] = item[0], s
new.append(" " + s) new.append(" " + s)
# if no non-optimizable attributes were found, convert to plain text # if no non-optimizable attributes were found, convert to plain text
......
...@@ -24,7 +24,7 @@ from cgi import escape ...@@ -24,7 +24,7 @@ from cgi import escape
from StringIO import StringIO from StringIO import StringIO
from DocumentTemplate.DT_Util import ustr from DocumentTemplate.DT_Util import ustr
from TALDefs import quote, TAL_VERSION, TALError, METALError from TALDefs import TAL_VERSION, TALError, METALError
from TALDefs import isCurrentVersion, getProgramVersion, getProgramMode from TALDefs import isCurrentVersion, getProgramVersion, getProgramMode
from TALGenerator import TALGenerator from TALGenerator import TALGenerator
from TranslationContext import TranslationContext from TranslationContext import TranslationContext
...@@ -120,7 +120,7 @@ class TALInterpreter: ...@@ -120,7 +120,7 @@ class TALInterpreter:
def StringIO(self): def StringIO(self):
# Third-party products wishing to provide a full Unicode-aware # Third-party products wishing to provide a full Unicode-aware
# StringIO can do so by monkey-patching this method. # StringIO can do so by monkey-patching this method.
return StringIO() return FasterStringIO()
def saveState(self): def saveState(self):
return (self.position, self.col, self.stream, return (self.position, self.col, self.stream,
...@@ -185,15 +185,20 @@ class TALInterpreter: ...@@ -185,15 +185,20 @@ class TALInterpreter:
bytecode_handlers = {} 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 oldlevel = self.level
self.level = oldlevel + 1 self.level = oldlevel + 1
handlers = self.dispatch handlers = self.dispatch
if tmpstream:
ostream = self.stream
owrite = self._stream_write
self.stream = tmpstream
self._stream_write = tmpstream.write
try: try:
if self.debug: if self.debug:
for (opcode, args) in program: for (opcode, args) in program:
...@@ -208,9 +213,6 @@ class TALInterpreter: ...@@ -208,9 +213,6 @@ class TALInterpreter:
handlers[opcode](self, args) handlers[opcode](self, args)
finally: finally:
self.level = oldlevel self.level = oldlevel
if tmpstream:
self.stream = ostream
self._stream_write = owrite
def do_version(self, version): def do_version(self, version):
assert version == TAL_VERSION assert version == TAL_VERSION
...@@ -246,8 +248,8 @@ class TALInterpreter: ...@@ -246,8 +248,8 @@ class TALInterpreter:
# for start tags with no attributes; those are optimized down # for start tags with no attributes; those are optimized down
# to rawtext events. Hence, there is no special "fast path" # to rawtext events. Hence, there is no special "fast path"
# for that case. # for that case.
_stream_write = self._stream_write L = ["<", name]
_stream_write("<" + name) append = L.append
col = self.col + _len(name) + 1 col = self.col + _len(name) + 1
wrap = self.wrap wrap = self.wrap
align = col + 1 align = col + 1
...@@ -266,13 +268,14 @@ class TALInterpreter: ...@@ -266,13 +268,14 @@ class TALInterpreter:
if (wrap and if (wrap and
col >= align and col >= align and
col + 1 + slen > wrap): col + 1 + slen > wrap):
_stream_write("\n" + " "*align) append("\n" + " "*align)
col = align + slen col = align + slen
else: else:
s = " " + s append(" ")
col = col + 1 + slen col = col + 1 + slen
_stream_write(s) append(s)
_stream_write(end) append(end)
self._stream_write("".join(L))
col = col + endlen col = col + endlen
finally: finally:
self.col = col self.col = col
...@@ -307,13 +310,13 @@ class TALInterpreter: ...@@ -307,13 +310,13 @@ class TALInterpreter:
if value is None: if value is None:
value = name value = name
else: else:
value = "%s=%s" % (name, quote(value)) value = '%s="%s"' % (name, escape(value, 1))
return 1, name, value return 1, name, value
def attrAction_tal(self, item): def attrAction_tal(self, item):
name, value, action = item[:3] if item[2] in ('metal', 'tal', 'xmlns', 'i18n'):
if action in ('metal', 'tal', 'xmlns', 'i18n'):
return self.attrAction(item) return self.attrAction(item)
name, value, action = item[:3]
ok = 1 ok = 1
expr, msgid = item[3:] expr, msgid = item[3:]
if self.html and name.lower() in BOOLEAN_HTML_ATTRS: if self.html and name.lower() in BOOLEAN_HTML_ATTRS:
...@@ -325,8 +328,7 @@ class TALInterpreter: ...@@ -325,8 +328,7 @@ class TALInterpreter:
value = None value = None
else: else:
ok = 0 ok = 0
else: elif expr is not None:
if expr is not None:
evalue = self.engine.evaluateText(item[3]) evalue = self.engine.evaluateText(item[3])
if evalue is self.Default: if evalue is self.Default:
if action == 'insert': # Cancelled insert if action == 'insert': # Cancelled insert
...@@ -335,22 +337,19 @@ class TALInterpreter: ...@@ -335,22 +337,19 @@ class TALInterpreter:
if evalue is None: if evalue is None:
ok = 0 ok = 0
value = evalue value = evalue
if ok:
if msgid: if msgid:
value = self.i18n_attribute(value) value = self.i18n_attribute(value)
if value is None: if value is None:
value = name value = name
value = "%s=%s" % (name, quote(value)) value = '%s="%s"' % (name, escape(value, 1))
return ok, name, value return ok, name, value
bytecode_handlers["<attrAction>"] = attrAction
def i18n_attribute(self, s): def i18n_attribute(self, s):
# s is the value of an attribute before translation # s is the value of an attribute before translation
# it may have been computed # it may have been computed
return self.translate(s, {}) return self.translate(s, {})
bytecode_handlers["<attrAction>"] = attrAction
def no_tag(self, start, program): def no_tag(self, start, program):
state = self.saveState() state = self.saveState()
self.stream = stream = self.StringIO() self.stream = stream = self.StringIO()
...@@ -391,7 +390,8 @@ class TALInterpreter: ...@@ -391,7 +390,8 @@ class TALInterpreter:
def do_rawtextBeginScope(self, (s, col, position, closeprev, dict)): def do_rawtextBeginScope(self, (s, col, position, closeprev, dict)):
self._stream_write(s) self._stream_write(s)
self.col = col self.col = col
self.do_setPosition(position) self.position = position
self.engine.setPosition(position)
if closeprev: if closeprev:
engine = self.engine engine = self.engine
engine.endScope() engine.endScope()
...@@ -403,7 +403,8 @@ class TALInterpreter: ...@@ -403,7 +403,8 @@ class TALInterpreter:
def do_rawtextBeginScope_tal(self, (s, col, position, closeprev, dict)): def do_rawtextBeginScope_tal(self, (s, col, position, closeprev, dict)):
self._stream_write(s) self._stream_write(s)
self.col = col self.col = col
self.do_setPosition(position) self.position = position
self.engine.setPosition(position)
engine = self.engine engine = self.engine
if closeprev: if closeprev:
engine.endScope() engine.endScope()
...@@ -481,7 +482,7 @@ class TALInterpreter: ...@@ -481,7 +482,7 @@ class TALInterpreter:
state = self.saveState() state = self.saveState()
try: try:
tmpstream = self.StringIO() tmpstream = self.StringIO()
self.interpret(program, tmpstream) self.interpretWithStream(program, tmpstream)
value = normalize(tmpstream.getvalue()) value = normalize(tmpstream.getvalue())
finally: finally:
self.restoreState(state) self.restoreState(state)
...@@ -516,7 +517,7 @@ class TALInterpreter: ...@@ -516,7 +517,7 @@ class TALInterpreter:
# Use a temporary stream to capture the interpretation of the # Use a temporary stream to capture the interpretation of the
# subnodes, which should /not/ go to the output stream. # subnodes, which should /not/ go to the output stream.
tmpstream = self.StringIO() 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 # We only care about the evaluated contents if we need an implicit
# message id. All other useful information will be in the i18ndict on # message id. All other useful information will be in the i18ndict on
# the top of the i18nStack. # the top of the i18nStack.
...@@ -726,6 +727,29 @@ class TALInterpreter: ...@@ -726,6 +727,29 @@ class TALInterpreter:
bytecode_handlers_tal["optTag"] = do_optTag_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
self.buflist.append(s)
self.len = self.pos = self.pos + len(s)
def _write_ValueError(s):
raise ValueError, "I/O operation on closed file"
def test(): def test():
from driver import FILE, parsefile from driver import FILE, parsefile
from DummyEngine import DummyEngine 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