diff PageTemplates/Expressions.py @ 984:a3a8ac7b8c40

Adding PageTemplates to the dist
author Richard Jones <richard@users.sourceforge.net>
date Fri, 30 Aug 2002 08:27:34 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PageTemplates/Expressions.py	Fri Aug 30 08:27:34 2002 +0000
@@ -0,0 +1,375 @@
+##############################################################################
+#
+# 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
+# 
+##############################################################################
+
+"""Page Template Expression Engine
+
+Page Template-specific implementation of TALES, with handlers
+for Python expressions, string literals, and paths.
+"""
+
+__version__='$Revision: 1.1 $'[11:-2]
+
+import re, sys
+from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
+     Undefined, Default, _parse_expr
+from string import strip, split, join, replace, lstrip
+from Acquisition import aq_base, aq_inner, aq_parent
+
+
+_engine = None
+def getEngine():
+    global _engine
+    if _engine is None:
+        from PathIterator import Iterator
+        _engine = Engine(Iterator)
+        installHandlers(_engine)
+    return _engine
+
+def installHandlers(engine):
+    reg = engine.registerType
+    pe = PathExpr
+    for pt in ('standard', 'path', 'exists', 'nocall'):
+        reg(pt, pe)
+    reg('string', StringExpr)
+    reg('python', PythonExpr)
+    reg('not', NotExpr)
+    reg('defer', DeferExpr)
+
+if sys.modules.has_key('Zope'):
+    import AccessControl
+    from AccessControl import getSecurityManager
+    try:
+        from AccessControl import Unauthorized
+    except ImportError:
+        Unauthorized = "Unauthorized"
+    if hasattr(AccessControl, 'full_read_guard'):
+        from ZRPythonExpr import PythonExpr, _SecureModuleImporter, \
+             call_with_ns
+    else:
+        from ZPythonExpr import PythonExpr, _SecureModuleImporter, \
+             call_with_ns
+else:
+    from PythonExpr import getSecurityManager, PythonExpr
+    try:
+        from zExceptions import Unauthorized
+    except ImportError:
+        Unauthorized = "Unauthorized"
+    def call_with_ns(f, ns, arg=1):
+        if arg==2:
+            return f(None, ns)
+        else:
+            return f(ns)
+
+    class _SecureModuleImporter:
+        """Simple version of the importer for use with trusted code."""
+        __allow_access_to_unprotected_subobjects__ = 1
+        def __getitem__(self, module):
+            __import__(module)
+            return sys.modules[module]
+
+SecureModuleImporter = _SecureModuleImporter()
+
+Undefs = (Undefined, AttributeError, KeyError,
+          TypeError, IndexError, Unauthorized)
+
+def render(ob, ns):
+    """
+    Calls the object, possibly a document template, or just returns it if
+    not callable.  (From DT_Util.py)
+    """
+    if hasattr(ob, '__render_with_namespace__'):
+        ob = call_with_ns(ob.__render_with_namespace__, ns)
+    else:
+        base = aq_base(ob)
+        if callable(base):
+            try:
+                if getattr(base, 'isDocTemp', 0):
+                    ob = call_with_ns(ob, ns, 2)
+                else:
+                    ob = ob()
+            except AttributeError, n:
+                if str(n) != '__call__':
+                    raise
+    return ob
+
+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
+        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()
+
+    def _eval(self, econtext,
+              list=list, isinstance=isinstance, StringType=type('')):
+        vars = econtext.vars
+        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:
+                    # 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
+            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.
+            ob = self._subexprs[-1](econtext)
+
+        if self._name == 'nocall' or isinstance(ob, StringType):
+            return ob
+        # Return the rendered object
+        return render(ob, econtext.vars)
+
+    def __call__(self, econtext):
+        if self._name == 'exists':
+            return self._exists(econtext)
+        return self._eval(econtext)
+
+    def __str__(self):
+        return '%s expression %s' % (self._name, `self._s`)
+
+    def __repr__(self):
+        return '%s:%s' % (self._name, `self._s`)
+
+            
+_interp = re.compile(r'\$(%(n)s)|\${(%(n)s(?:/%(n)s)*)}' % {'n': NAME_RE})
+
+class StringExpr:
+    def __init__(self, name, expr, engine):
+        self._s = expr
+        if '%' in expr:
+            expr = replace(expr, '%', '%%')
+        self._vars = vars = []
+        if '$' in expr:
+            parts = []
+            for exp in split(expr, '$$'):
+                if parts: parts.append('$')
+                m = _interp.search(exp)
+                while m is not None:
+                    parts.append(exp[:m.start()])
+                    parts.append('%s')
+                    vars.append(PathExpr('path', m.group(1) or m.group(2),
+                                         engine))
+                    exp = exp[m.end():]
+                    m = _interp.search(exp)
+                if '$' in exp:
+                    raise CompilerError, (
+                        '$ must be doubled or followed by a simple path')
+                parts.append(exp)
+            expr = join(parts, '')
+        self._expr = expr
+        
+    def __call__(self, econtext):
+        vvals = []
+        for var in self._vars:
+            v = var(econtext)
+            if isinstance(v, Exception):
+                raise v
+            vvals.append(v)
+        return self._expr % tuple(vvals)
+
+    def __str__(self):
+        return 'string expression %s' % `self._s`
+
+    def __repr__(self):
+        return 'string:%s' % `self._s`
+
+class NotExpr:
+    def __init__(self, name, expr, compiler):
+        self._s = expr = lstrip(expr)
+        self._c = compiler.compile(expr)
+        
+    def __call__(self, econtext):
+        return not econtext.evaluateBoolean(self._c)
+
+    def __repr__(self):
+        return 'not:%s' % `self._s`
+
+class DeferWrapper:
+    def __init__(self, expr, econtext):
+        self._expr = expr
+        self._econtext = econtext
+
+    def __str__(self):
+        return str(self())
+
+    def __call__(self):
+        return self._expr(self._econtext)
+
+class DeferExpr:
+    def __init__(self, name, expr, compiler):
+        self._s = expr = lstrip(expr)
+        self._c = compiler.compile(expr)
+        
+    def __call__(self, econtext):
+        return DeferWrapper(self._c, econtext)
+
+    def __repr__(self):
+        return 'defer:%s' % `self._s`
+
+
+def restrictedTraverse(self, path, securityManager,
+                       get=getattr, has=hasattr, N=None, M=[],
+                       TupleType=type(()) ):
+
+    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
+        path.pop(0)
+        
+    path.reverse()
+    validate = securityManager.validate
+    object = self
+    #print 'TRAVERSE', (object, path)
+    while path:
+        __traceback_info__ = REQUEST
+        name = path.pop()
+
+        if isinstance(name, TupleType):
+            object = apply(object, name)
+            continue
+
+        if name[0] == '_':
+            # Never allowed in a URL.
+            raise AttributeError, name
+
+        if name=='..':
+            o = get(object, 'aq_parent', M)
+            if o is not M:
+                if not validate(object, object, name, o):
+                    raise Unauthorized, name
+                object=o
+                continue
+
+        t = get(object, '__bobo_traverse__', N)
+        if t is not N:
+            o=t(REQUEST, name)
+                    
+            container = None
+            if has(o, 'im_self'):
+                container = o.im_self
+            elif (has(get(object, 'aq_base', object), name)
+                and get(object, name) == o):
+                container = object
+            if not validate(object, container, name, o):
+                raise Unauthorized, name
+        else:
+            # Try an attribute.
+            o = get(object, name, M)
+        #    print '...', (object, name, M, o)
+            if o is not M:
+                # Check access to the attribute.
+                if has(object, 'aq_acquire'):
+                    object.aq_acquire(
+                        name, validate2, validate)
+                else:
+                    if not validate(object, object, name, o):
+                        raise Unauthorized, name
+            else:
+                # Try an item.
+                try:
+                    # XXX maybe in Python 2.2 we can just check whether
+                    # the object has the attribute "__getitem__"
+                    # instead of blindly catching exceptions.
+        #            print 'Try an item', (object, name)
+                    o = object[name]
+                except AttributeError, exc:
+                    if str(exc).find('__getitem__') >= 0:
+                        # The object does not support the item interface.
+                        # Try to re-raise the original attribute error.
+                        # XXX I think this only happens with
+                        # ExtensionClass instances.
+                        get(object, name)
+                    raise
+                except TypeError, exc:
+                    if str(exc).find('unsubscriptable') >= 0:
+                        # The object does not support the item interface.
+                        # Try to re-raise the original attribute error.
+                        # XXX This is sooooo ugly.
+                        get(object, name)
+                    raise
+                else:
+                    # Check access to the item.
+                    if not validate(object, object, name, o):
+                        raise Unauthorized, name
+        #print '... object is now', `o`
+        object = o
+
+    return object
+
+
+def validate2(orig, inst, name, v, real_validate):
+    if not real_validate(orig, inst, name, v):
+        raise Unauthorized, name
+    return 1
+

Roundup Issue Tracker: http://roundup-tracker.org/