[Zope-Checkins] CVS: Releases/Zope/lib/python/Products/PageTemplates - PathIterator.py:1.1 Expressions.py:1.30 PageTemplate.py:1.20

Evan Simpson evan@zope.com
Thu, 13 Dec 2001 13:38:20 -0500


Update of /cvs-repository/Releases/Zope/lib/python/Products/PageTemplates
In directory cvs.zope.org:/tmp/cvs-serv21379

Modified Files:
	Expressions.py PageTemplate.py 
Added Files:
	PathIterator.py 
Log Message:
Refactor Path expressions, make them support other expression types as the final alternate, and support Iterator.first() and last().


=== Added File Releases/Zope/lib/python/Products/PageTemplates/PathIterator.py ===
##############################################################################
#
# Copyright (c) 2001 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
# 
##############################################################################

"""Path Iterator

A TALES Iterator with the ability to use first() and last() on
subpaths of elements.
"""

__version__='$Revision: 1.1 $'[11:-2]

import TALES
from Expressions import restrictedTraverse, Undefs, getSecurityManager

class Iterator(TALES.Iterator):
    def __bobo_traverse__(self, REQUEST, name):
        if name in ('first', 'last'):
            path = REQUEST['TraversalRequestNameStack']
            names = list(path)
            del path[:]
            names.reverse()
            return getattr(self, name)(names)
        return getattr(self, name)
            
    def same_part(self, name, ob1, ob2):
        if name is None:
            return ob1 == ob2
        if isinstance(name, type('')):
            name = split(name, '/')
        name = filter(None, name)
        securityManager = getSecurityManager()
        try:
            ob1 = restrictedTraverse(ob1, name, securityManager)
            ob2 = restrictedTraverse(ob2, name, securityManager)
        except Undefs:
            return 0
        return ob1 == ob2


=== Releases/Zope/lib/python/Products/PageTemplates/Expressions.py 1.29 => 1.30 ===
 import re, sys
 from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
-     TALESError, Undefined, Default
+     TALESError, Undefined, Default, _parse_expr
 from string import strip, split, join, replace, lstrip
 from Acquisition import aq_base, aq_inner, aq_parent
 
@@ -30,7 +30,8 @@
 def getEngine():
     global _engine
     if _engine is None:
-        _engine = Engine()
+        from PathIterator import Iterator
+        _engine = Engine(Iterator)
         installHandlers(_engine)
         _engine._nocatch = (TALESError, 'Redirect')
     return _engine
@@ -71,6 +72,9 @@
         else:
             return f(ns)
     
+Undefs = (Undefined, AttributeError, KeyError,
+          TypeError, IndexError, Unauthorized)
+
 def render(ob, ns):
     """
     Calls the object, possibly a document template, or just returns it if
@@ -91,75 +95,98 @@
                     raise
     return ob
 
-class PathExpr:
-    def __init__(self, name, expr, engine):
-        self._s = expr
-        self._name = name
-        self._paths = map(self._prepPath, split(expr, '|'))
-
-    def _prepPath(self, path):
-        path = split(strip(path), '/')
-        base = path.pop(0)
+class SubPathExpr:
+    def __init__(self, path):
+        self._path = path = split(strip(path), '/')
+        self._base = base = path.pop(0)
         if not _valid_name(base):
             raise CompilerError, 'Invalid variable name "%s"' % base
         # Parse path
-        dp = []
+        self._dp = dp = []
         for i in range(len(path)):
             e = path[i]
             if e[:1] == '?' and _valid_name(e[1:]):
                 dp.append((i, e[1:]))
         dp.reverse()
-        return base, path, dp
 
-    def _eval(self, econtext, securityManager,
-              list=list, isinstance=isinstance, StringType=type(''),
-              render=render):
+    def _eval(self, econtext,
+              list=list, isinstance=isinstance, StringType=type('')):
         vars = econtext.vars
-        exists = 0
-        more_paths = len(self._paths)
-        for base, path, dp in self._paths:
-            more_paths = more_paths - 1
-            # Expand dynamic path parts from right to left.
-            if dp:
-                path = list(path) # Copy!
-                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
+        path = self._path
+        if self._dp:
+            path = list(path) # Copy!
+            for i, varname in self._dp:
+                val = vars[varname]
+                if isinstance(val, StringType):
+                    path[i] = val
                 else:
-                    ob = vars[base]
-                if isinstance(ob, DeferWrapper):
-                    ob = ob()
-                if path:
-                    ob = restrictedTraverse(ob, path, securityManager)
-                exists = 1
+                    # If the value isn't a string, assume it's a sequence
+                    # of path names.
+                    path[i:i+1] = list(val)
+        __traceback_info__ = base = self._base
+        if base == 'CONTEXTS':
+            ob = econtext.contexts
+        else:
+            ob = vars[base]
+        if isinstance(ob, DeferWrapper):
+            ob = ob()
+        if path:
+            ob = restrictedTraverse(ob, path, getSecurityManager())
+        return ob
+
+class PathExpr:
+    def __init__(self, name, expr, engine):
+        self._s = expr
+        self._name = name
+        paths = split(expr, '|')
+        self._subexprs = []
+        add = self._subexprs.append
+        for i in range(len(paths)):
+            path = lstrip(paths[i])
+            if _parse_expr(path):
+                # This part is the start of another expression type,
+                # so glue it back together and compile it.
+                add(engine.compile(lstrip(join(paths[i:], '|'))))
                 break
-            except Undefined:
-                if self._name != 'exists' and not more_paths:
-                    raise
-            except (AttributeError, KeyError, TypeError, IndexError,
-                    Unauthorized), e:
-                if self._name != 'exists' and not more_paths:
-                    raise Undefined(self._s, sys.exc_info())
+            add(SubPathExpr(path)._eval)
+
+    def _exists(self, econtext):
+        for expr in self._subexprs:
+            try:
+                expr(econtext)
+            except Undefs:
+                pass
+            else:
+                return 1
+        return 0
+
+    def _eval(self, econtext,
+              isinstance=isinstance, StringType=type(''), render=render):
+        for expr in self._subexprs[:-1]:
+            # Try all but the last subexpression, skipping undefined ones
+            try:
+                ob = expr(econtext)
+            except Undefs:
+                pass
+            else:
+                break
+        else:
+            # On the last subexpression allow exceptions through, but
+            # wrap ones that indicate that the subexpression was undefined
+            try:
+                ob = self._subexprs[-1](econtext)
+            except Undefs[1:]:
+                raise 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, vars)
+        return render(ob, econtext.vars)
 
     def __call__(self, econtext):
-        return self._eval(econtext, getSecurityManager())
+        if self._name == 'exists':
+            return self._exists(econtext)
+        return self._eval(econtext)
 
     def __str__(self):
         return '%s expression %s' % (self._name, `self._s`)
@@ -247,21 +274,21 @@
 def restrictedTraverse(self, path, securityManager,
                        get=getattr, has=hasattr, N=None, M=[]):
 
-    i = 0
+    REQUEST = {'path': path}
+    REQUEST['TraversalRequestNameStack'] = path = path[:] # Copy!
     if not path[0]:
         # If the path starts with an empty string, go to the root first.
         self = self.getPhysicalRoot()
         if not securityManager.validateValue(self):
             raise Unauthorized, name
-        i = 1
-
-    REQUEST={'TraversalRequestNameStack': path}
+        path.pop(0)
+        
+    path.reverse()
     validate = securityManager.validate
     object = self
-    while i < len(path):
-        __traceback_info__ = (path, i)
-        name = path[i]
-        i = i + 1
+    while path:
+        __traceback_info__ = REQUEST
+        name = path.pop()
 
         if name[0] == '_':
             # Never allowed in a URL.


=== Releases/Zope/lib/python/Products/PageTemplates/PageTemplate.py 1.19 => 1.20 ===
     _v_errors = ()
     _v_warnings = ()
+    id = '(unknown)'
     _text = ''
     _error_start = '<!-- Page Template Diagnostics'