[ZPT] CVS: Packages/Products/PageTemplates (Products/DC/PageTemplates) - HISTORY.txt:1.1.2.1 CHANGES.txt:1.6.2.2 Expressions.py:1.13.2.2 PageTemplate.py:1.11.2.1 PageTemplateFile.py:1.1.2.2 PythonExpr.py:1.2.2.2 README.txt:1.1.6.1 TALES.py:1.13.2.1 ZPythonExpr.py:1.2.2.2 ZRPythonExpr.py:1.2.2.2 ZopePageTemplate.py:1.9.2.2 version.txt:1.6.2.2

evan@serenade.digicool.com evan@serenade.digicool.com
Sat, 16 Jun 2001 11:55:59 -0400


Update of /cvs-repository/Packages/Products/PageTemplates
In directory serenade:/home/evan/Zope/pt/lib/python/Products/PageTemplates

Modified Files:
      Tag: zpt-1_3_0
	CHANGES.txt Expressions.py PageTemplate.py PageTemplateFile.py 
	PythonExpr.py README.txt TALES.py ZPythonExpr.py 
	ZRPythonExpr.py ZopePageTemplate.py version.txt 
Added Files:
      Tag: zpt-1_3_0
	HISTORY.txt 
Log Message:
Merge trunk changes for release 1.3.2



--- Added File HISTORY.txt in package Packages/Products/PageTemplates ---
Page Template history

  This file contains change information for previous versions of
  PageTemplates. Change information for the current release can be found
  in the file CHANGES.txt.

    Version 1.3.1

      Features Added

        - Added error logging to PageTemplateFiles.
        - Refactored PythonExpr, and added support for Zope 2.4

    Version 1.3.0

      Features Added

        - New builtin variables 'default', 'user', and 'attrs'.
        - Removed path modifiers.
        - Added '|' operator for paths.
        - Tweaked parameters passed when calling DTML.
        - Expression types now have corresponding builtin functions in
          Python expressions.

    Version 1.2.1

      Bug Fixed

        - 'repeat' variable access was broken.

    Version 1.2.0

      Features Added

        - Depends on the new ZTUtils package, which adds batching and
          tree widget capabilities.
        - Path expressions now have optional path modifiers.  These
          appear in parenthesis before the path, and include 'if',
          'exists', and 'nocall'.
        - Changed nocall: and exists: expressions types into path modifiers.
        - The 'if' path modifier can cancel any TAL action.

    Version 1.1.0

      Features Added
        - Changed tests to match TAL's omitted attributes.

    Version 1.0.0

        - Various minor bugs fixed

    Version 1.0.0b1

	- All functionality described in the Project Wiki is implemented

--- Updated File CHANGES.txt in package Packages/Products/PageTemplates --
--- CHANGES.txt	2001/05/25 14:37:21	1.6.2.1
+++ CHANGES.txt	2001/06/16 15:55:28	1.6.2.2
@@ -1,51 +1,18 @@
-2001-05-25  Evan Simpson <evan@digicool.com>
+Page Template changes
 
-        * Version 1.3.1
-        * Added error logging to PageTemplateFiles.
-        * Refactored PythonExpr, and added support for Zope 2.4
+  This file contains change information for the current release. 
+  Change information for previous versions can be found in the
+  file HISTORY.txt.
 
-2001-05-21  Evan Simpson <evan@digicool.com>
+    Version 1.3.2
 
-        * Version 1.3.0
-        * Guido fixed use of nested macros.
-        * New builtin variables 'default', 'user', and 'attrs'.
-        * Removed path modifiers.
-        * Added '|' operator for paths.
-        * Tweaked parameters passed when calling DTML.
-        * Expression types now have corresponding builtin functions in
-          Python expressions.
+      Features Added
 
-2001-05-07  Evan Simpson <evan@digicool.com>
+        - Adopted Zope-style CHANGES.txt and HISTORY.txt
+        - Improved execution performance
+        - nocall: paths are back in.
 
-        * Version 1.2.1
-        * Bugfixes for 'repeat' variable access
+      Bugs Fixed
 
-2001-04-27  Evan Simpson <evan@digicool.com>
-
-        * Version 1.2.0
-        * Depends on the new ZTUtils package, which adds batching and
-          tree widget capabilities.
-        * Path expressions now have optional path modifiers.  These
-          appear in parenthesis before the path, and include 'if',
-          'exists', and 'nocall'.
-        * Changed nocall: and exists: expressions types into path modifiers.
-        * tal:attributes no longer inserts empty attributes into
-          source.
-        * The 'if' path modifier can cancel any TAL action.
-
-2001-04-10  Evan Simpson <evan@digicool.com>
-
-        * Version 1.1.0
-        * Bug fixes
-        * Tell TAL not to parse replacement structural text
-        * Change tests to match TAL's omitted attributes
-
-2001-03-30  Evan Simpson <evan@digicool.com>
-
-        * Version 1.0.0
-        * Minor bugs fixed
-
-2001-03-27  Evan Simpson <evan@digicool.com>
-
-	* Version 1.0.0b1
-	* All functionality described in the Project Wiki is implemented
+        - TALES expressions let any string exception through, not just
+          Redirect and Unauthorized.

--- Updated File Expressions.py in package Packages/Products/PageTemplates --
--- Expressions.py	2001/05/25 14:37:21	1.13.2.1
+++ Expressions.py	2001/06/16 15:55:28	1.13.2.2
@@ -108,7 +108,7 @@
 def installHandlers(engine):
     reg = engine.registerType
     pe = PathExpr
-    for pt in ('standard', 'path', 'exists'):
+    for pt in ('standard', 'path', 'exists', 'nocall'):
         reg(pt, pe)
     reg('string', StringExpr)
     reg('python', PythonExpr)
@@ -171,45 +171,46 @@
         dp.reverse()
         return base, path, dp
 
-    def _eval(self, (base, path, dp), econtext):
-        path = list(path) # Copy!
-        contexts = econtext.contexts
-        var = contexts['var']
-        # Expand dynamic path parts from right to left.
-        for i, varname in dp:
-            val = var[varname]
-            if type(val) is type(''):
-                path[i] = val
-            else:
-                # If the value isn't a string, assume it's a sequence
-                # of path names.
-                path[i:i+1] = list(val)
-        try:
-            __traceback_info__ = base
-            if base == 'CONTEXTS':
-                ob = contexts
-            else:
-                has, ob = var.has_get(base)
-                if not has:
-                    ob = contexts[base]
-            return restrictedTraverse(ob, path)
-        except (AttributeError, KeyError, TypeError, IndexError,
-                'Unauthorized'), e:
-            return Undefined(self._s, sys.exc_info())
-
-    def __call__(self, econtext):
-        for pathinfo in self._paths:
-            ob = self._eval(pathinfo, econtext)
-            exists = not isinstance(ob, Undefined)
-            
-            if exists:
-                # We're done
+    def _eval(self, econtext, securityManager,
+              list=list, isinstance=isinstance, StringType=type(''),
+              render=render):
+        vars = econtext.vars
+        exists = 0
+        for base, path, dp in self._paths:
+            path = list(path) # Copy!
+            # Expand dynamic path parts from right to left.
+            for i, varname in dp:
+                val = vars[varname]
+                if isinstance(val, StringType):
+                    path[i] = val
+                else:
+                    # If the value isn't a string, assume it's a sequence
+                    # of path names.
+                    path[i:i+1] = list(val)
+            try:
+                __traceback_info__ = base
+                if base == 'CONTEXTS':
+                    ob = econtext.contexts
+                else:
+                    ob = vars[base]
+                if path:
+                    ob = restrictedTraverse(ob, path, securityManager)
+                exists = 1
                 break
+            except (AttributeError, KeyError, TypeError, IndexError,
+                    'Unauthorized'), e:
+                ob = Undefined(self._s, sys.exc_info())
+
         if self._name == 'exists':
             # All we wanted to know is whether one of the paths exist.
             return exists
+        if self._name == 'nocall' or isinstance(ob, StringType):
+            return ob
         # Return the rendered object
-        return render(ob, econtext.contexts)
+        return render(ob, vars)
+
+    def __call__(self, econtext):
+        return self._eval(econtext, getSecurityManager())
 
     def __str__(self):
         return '%s expression "%s"' % (self._name, self._s)
@@ -249,7 +250,10 @@
     def __call__(self, econtext):
         vvals = []
         for var in self._vars:
-            vvals.append(var(econtext))
+            v = var(econtext)
+            if isinstance(v, Exception):
+                raise v
+            vvals.append(v)
         return self._expr % tuple(vvals)
 
     def __str__(self):
@@ -270,26 +274,20 @@
         return '<NotExpr %s>' % `self._s`
 
 
-def restrictedTraverse(self, path):
+def restrictedTraverse(self, path, securityManager,
+                       get=getattr, has=hasattr, N=None, M=[]):
 
-    if not path: return self
-
-    get=getattr
-    N=None
-    M=[] #marker
-
-    REQUEST={'TraversalRequestNameStack': path}
-    securityManager = getSecurityManager()
-    
-    plen = len(path)
     i = 0
     if not path[0]:
         # If the path starts with an empty string, go to the root first.
-        i = 1
         self = self.getPhysicalRoot()
         if not securityManager.validateValue(self):
             raise 'Unauthorized', name
-                    
+        i = 1
+
+    plen = len(path)
+    REQUEST={'TraversalRequestNameStack': path}
+    validate = securityManager.validate
     object = self
     while i < plen:
         __traceback_info__ = (path, i)
@@ -301,9 +299,9 @@
             raise AttributeError, name
 
         if name=='..':
-            o = getattr(object, 'aq_parent', M)
+            o = get(object, 'aq_parent', M)
             if o is not M:
-                if not securityManager.validate(object, object, name, o):
+                if not validate(object, object, name, o):
                     raise 'Unauthorized', name
                 object=o
                 continue
@@ -313,27 +311,27 @@
             o=t(REQUEST, name)
                     
             container = None
-            if (hasattr(get(object, 'aq_base', object), name)
+            if (has(get(object, 'aq_base', object), name)
                 and get(object, name) is o):
                 container = object
-            if not securityManager.validate(object, container, name, o):
+            if not validate(object, container, name, o):
                 raise 'Unauthorized', name
         else:
             o=get(object, name, M)
             if o is not M:
                 # Check security.
-                if hasattr(object, 'aq_acquire'):
+                if has(object, 'aq_acquire'):
                     object.aq_acquire(
-                        name, validate2, securityManager.validate)
+                        name, validate2, validate)
                 else:
-                    if not securityManager.validate(object, object, name, o):
+                    if not validate(object, object, name, o):
                         raise 'Unauthorized', name
             else:
                 try:
                     o=object[name]
                 except (AttributeError, TypeError):
                     raise AttributeError, name
-                if not securityManager.validate(object, object, name, o):
+                if not validate(object, object, name, o):
                     raise 'Unauthorized', name
         object = o
 

--- Updated File PageTemplate.py in package Packages/Products/PageTemplates --
--- PageTemplate.py	2001/05/18 18:13:02	1.11
+++ PageTemplate.py	2001/06/16 15:55:28	1.11.2.1
@@ -99,6 +99,8 @@
 from cStringIO import StringIO
 from ExtensionClass import Base
 
+Z_DEBUG_MODE = os.environ.get('Z_DEBUG_MODE') == '1'
+
 class MacroCollection(Base):
     def __of__(self, parent):
         return parent._v_macros
@@ -145,7 +147,7 @@
         output = StringIO()
         c = self.pt_getContext()
         c.update(extra_context)
-        if __debug__:
+        if Z_DEBUG_MODE:
             __traceback_info__ = pprint.pformat(c)
 
         TALInterpreter(self._v_program, self._v_macros,

--- Updated File PageTemplateFile.py in package Packages/Products/PageTemplates --

--- Updated File PythonExpr.py in package Packages/Products/PageTemplates --
--- PythonExpr.py	2001/05/25 14:37:21	1.2.2.1
+++ PythonExpr.py	2001/06/16 15:55:28	1.2.2.2
@@ -117,10 +117,10 @@
     def _bind_used_names(self, econtext):
         # Bind template variables
         names = {}
-        var = econtext.contexts['var']
+        vars = econtext.vars
         getType = econtext._engine.getTypes().get
         for vname in self._f_varnames:
-            has, val = var.has_get(vname)
+            has, val = vars.has_get(vname)
             if not has:
                 has = val = getType(vname)
                 if has:

--- Updated File README.txt in package Packages/Products/PageTemplates --
--- README.txt	2001/03/29 10:21:26	1.1
+++ README.txt	2001/06/16 15:55:28	1.1.6.1
@@ -1,2 +1,4 @@
 See <a href="http://dev.zope.org/Wikis/DevSite/Projects/ZPT">the
-ZPT project Wiki</a> for more information.
+ZPT project Wiki</a> for more information about Page Templates, or
+<a href="http://www.zope.org/Members/4am/ZPT">the download page</a>
+for installation instructions and the most recent version of the software.

--- Updated File TALES.py in package Packages/Products/PageTemplates --
--- TALES.py	2001/05/18 18:11:19	1.13
+++ TALES.py	2001/06/16 15:55:28	1.13.2.1
@@ -92,6 +92,8 @@
 import re, sys, ZTUtils
 from MultiMapping import MultiMapping
 
+StringType = type('')
+
 NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
 _parse_expr = re.compile(r"(%s):(.*)" % NAME_RE).match
 _valid_name = re.compile('%s$' % NAME_RE).match
@@ -141,19 +143,13 @@
     '''
     __allow_access_to_unprotected_subobjects__ = 1
     push = pop = None
-    def _push(self, ob):
-        MultiMapping.push(self, ob)
-    def _pop(self, *args):
-        if args:
-            return apply(MultiMapping.pop, (self,) + args)
-        else:
-            return MultiMapping.pop(self)
-    def has_get(self, key):
+
+    _push = MultiMapping.push
+    _pop = MultiMapping.pop
+
+    def has_get(self, key, _marker=[]):
         v = self.get(key, _marker)
-        if v is _marker:
-            return 0, None
-        else:
-            return 1, v
+        return v is not _marker, v
 
 class Iterator(ZTUtils.Iterator):
     def __init__(self, name, seq, context):
@@ -234,83 +230,87 @@
         contexts['nothing'] = None
         contexts['default'] = Default
 
-        # Keep track of what contexts get pushed as each scope begins.
-        self._ctxts_pushed = []
-        # These contexts will need to be pushed.
-        self._current_ctxts = {'local': 1, 'repeat': 1}
-        
-        lv = self._context_class()
-        init_local = contexts.get('local', None)
-        if init_local:
-            lv._push(init_local)
-        contexts['local'] = lv
-        contexts['repeat'] = rep =  self._context_class()
+        self.repeat_vars = rv = {}
+        # Wrap this, as it is visible to restricted code
+        contexts['repeat'] = rep =  self._context_class(rv)
         contexts['loop'] = rep # alias
-        contexts['global'] = gv = contexts.copy()
-        contexts['var'] = self._context_class(gv, lv)
-        
+
+        self.global_vars = gv = contexts.copy()
+        self.local_vars = lv = {}
+        self.vars = self._context_class(gv, lv)
+
+        # Keep track of what needs to be popped as each scope ends.
+        self._scope_stack = []
+
     def beginScope(self):
-        oldctxts = self._current_ctxts
-        self._ctxts_pushed.append(oldctxts)
-        self._current_ctxts = ctxts = {}
-        for ctxname in oldctxts.keys():
-            # Push fresh namespace on each local stack.
-            ctxts[ctxname] = ctx = {}
-            self.contexts[ctxname]._push(ctx)
+        self._scope_stack.append([self.local_vars.copy()])
 
     def endScope(self):
-        self._current_ctxts = ctxts = self._ctxts_pushed.pop()
-        # Pop the ones that were pushed at the beginning of the scope.
-        for ctxname in ctxts.keys():
-            ctx = self.contexts[ctxname]._pop()
-            # Make sure there's no circular garbage
-            ctx.clear()
+        scope = self._scope_stack.pop()
+        self.local_vars = lv = scope[0]
+        v = self.vars
+        v._pop()
+        v._push(lv)
+        # Pop repeat variables, if any
+        i = len(scope) - 1
+        while i:
+            name, value = scope[i]
+            if value is None:
+                del self.repeat_vars[name]
+            else:
+                self.repeat_vars[name] = value
+            i = i - 1
 
     def setLocal(self, name, value):
-        self._current_ctxts['local'][name] = value
+        self.local_vars[name] = value
 
     def setGlobal(self, name, value):
-        self.contexts['global'][name] = value
+        self.global_vars[name] = value
 
     def setRepeat(self, name, expr):
         expr = self.evaluate(expr)
         it = self._engine.Iterator(name, expr, self)
-        self._current_ctxts['repeat'][name] = it
+        old_value = self.repeat_vars.get(name)
+        self._scope_stack[-1].append((name, old_value))
+        self.repeat_vars[name] = it
         return it
 
-    def evaluate(self, expression):
-        if type(expression) is type(''):
+    def evaluate(self, expression,
+                 isinstance=isinstance, StringType=StringType):
+        if isinstance(expression, StringType):
             expression = self._engine.compile(expression)
         try:
             v = expression(self)
-            if isinstance(v, Exception):
-                raise v
-            return v
         except TALESError:
             raise
         except:
-            if type(sys.exc_info()[0]) is type(''):
+            if sys.exc_info()[0] in ('Redirect', 'Unauthorized'):
                 raise
             raise TALESError, (`expression`, sys.exc_info()), sys.exc_info()[2]
+        else:
+            if isinstance(v, Exception):
+                raise v
+            return v
 
     evaluateValue = evaluate
 
     def evaluateBoolean(self, expr):
-        bool = self.evaluate(expr)
-        return not not bool
+        return not not self.evaluate(expr)
 
-    def evaluateText(self, expr):
+    def evaluateText(self, expr, None=None):
         text = self.evaluate(expr)
-        if text not in (None, Default):
-            text = str(text)
-        return text
+        if text is Default or text is None:
+            return text
+        return str(text)
 
     def evaluateStructure(self, expr):
         return self.evaluate(expr)
+    evaluateStructure = evaluate
 
     def evaluateMacro(self, expr):
         # XXX Should return None or a macro definition
         return self.evaluate(expr)
+    evaluateMacro = evaluate
 
     def getTALESError(self):
         return TALESError
@@ -327,9 +327,4 @@
         return self._name, self._expr
     def __repr__(self):
         return '<SimpleExpr %s %s>' % (self._name, `self._expr`)
-
-
-
-
-
 

--- Updated File ZPythonExpr.py in package Packages/Products/PageTemplates --
--- ZPythonExpr.py	2001/05/25 14:37:21	1.2.2.1
+++ ZPythonExpr.py	2001/06/16 15:55:28	1.2.2.2
@@ -128,7 +128,7 @@
     td.this = None
     td._push(ns['request'])
     td._push(InstanceDict(ns['here'], td))
-    td._push(ns['var'])
+    td._push(ns)
     try:
         if arg==2:
             return f(None, td)

--- Updated File ZRPythonExpr.py in package Packages/Products/PageTemplates --
--- ZRPythonExpr.py	2001/05/25 14:37:21	1.2.2.1
+++ ZRPythonExpr.py	2001/06/16 15:55:28	1.2.2.2
@@ -133,7 +133,7 @@
     td.this = None
     td._push(ns['request'])
     td._push(InstanceDict(ns['here'], td, full_read_guard))
-    td._push(ns['var'])
+    td._push(ns)
     try:
         if arg==2:
             return f(None, td)

--- Updated File ZopePageTemplate.py in package Packages/Products/PageTemplates --

--- Updated File version.txt in package Packages/Products/PageTemplates --
--- version.txt	2001/05/25 14:37:21	1.6.2.1
+++ version.txt	2001/06/16 15:55:28	1.6.2.2
@@ -1 +1 @@
-PageTemplates-1-3-1
+PageTemplates-1-3-2