diff roundup/cgi/PageTemplates/TALES.py @ 1356:83f33642d220 maint-0.5

[[Metadata associated with this commit was garbled during conversion from CVS to Subversion.]]
author Richard Jones <richard@users.sourceforge.net>
date Thu, 09 Jan 2003 22:59:22 +0000
parents
children d6177306d010
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/roundup/cgi/PageTemplates/TALES.py	Thu Jan 09 22:59:22 2003 +0000
@@ -0,0 +1,291 @@
+##############################################################################
+#
+# 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
+# 
+##############################################################################
+"""TALES
+
+An implementation of a generic TALES engine
+
+Modified for Roundup 0.5 release:
+
+- changed imports to import from roundup.cgi
+"""
+
+__version__='$Revision: 1.4 $'[11:-2]
+
+import re, sys
+from roundup.cgi import 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
+
+class TALESError(Exception):
+    """Error during TALES expression evaluation"""
+
+class Undefined(TALESError):
+    '''Exception raised on traversal of an undefined path'''
+
+class RegistrationError(Exception):
+    '''TALES Type Registration Error'''
+
+class CompilerError(Exception):
+    '''TALES Compiler Error'''
+
+class Default:
+    '''Retain Default'''
+Default = Default()
+
+_marker = []
+
+class SafeMapping(MultiMapping):
+    '''Mapping with security declarations and limited method exposure.
+
+    Since it subclasses MultiMapping, this class can be used to wrap
+    one or more mapping objects.  Restricted Python code will not be
+    able to mutate the SafeMapping or the wrapped mappings, but will be
+    able to read any value.
+    '''
+    __allow_access_to_unprotected_subobjects__ = 1
+    push = pop = None
+
+    _push = MultiMapping.push
+    _pop = MultiMapping.pop
+
+    def has_get(self, key, _marker=[]):
+        v = self.get(key, _marker)
+        return v is not _marker, v
+
+class Iterator(ZTUtils.Iterator):
+    def __init__(self, name, seq, context):
+        ZTUtils.Iterator.__init__(self, seq)
+        self.name = name
+        self._context = context
+
+    def next(self):
+        if ZTUtils.Iterator.next(self):
+            self._context.setLocal(self.name, self.item)
+            return 1
+        return 0
+
+
+class ErrorInfo:
+    """Information about an exception passed to an on-error handler."""
+    __allow_access_to_unprotected_subobjects__ = 1
+
+    def __init__(self, err, position=(None, None)):
+        if isinstance(err, Exception):
+            self.type = err.__class__
+            self.value = err
+        else:
+            self.type = err
+            self.value = None
+        self.lineno = position[0]
+        self.offset = position[1]
+
+
+class Engine:
+    '''Expression Engine
+
+    An instance of this class keeps a mutable collection of expression
+    type handlers.  It can compile expression strings by delegating to
+    these handlers.  It can provide an expression Context, which is
+    capable of holding state and evaluating compiled expressions.
+    '''
+    Iterator = Iterator
+
+    def __init__(self, Iterator=None):
+        self.types = {}
+        if Iterator is not None:
+            self.Iterator = Iterator
+
+    def registerType(self, name, handler):
+        if not _valid_name(name):
+            raise RegistrationError, 'Invalid Expression type "%s".' % name
+        types = self.types
+        if types.has_key(name):
+            raise RegistrationError, (
+                'Multiple registrations for Expression type "%s".' %
+                name)
+        types[name] = handler
+
+    def getTypes(self):
+        return self.types
+
+    def compile(self, expression):
+        m = _parse_expr(expression)
+        if m:
+            type = m.group(1)
+            expr = expression[m.end():]
+        else:
+            type = "standard"
+            expr = expression
+        try:
+            handler = self.types[type]
+        except KeyError:
+            raise CompilerError, (
+                'Unrecognized expression type "%s".' % type)
+        return handler(type, expr, self)
+    
+    def getContext(self, contexts=None, **kwcontexts):
+        if contexts is not None:
+            if kwcontexts:
+                kwcontexts.update(contexts)
+            else:
+                kwcontexts = contexts
+        return Context(self, kwcontexts)
+
+    def getCompilerError(self):
+        return CompilerError
+
+class Context:
+    '''Expression Context
+
+    An instance of this class holds context information that it can
+    use to evaluate compiled expressions.
+    '''
+
+    _context_class = SafeMapping
+    position = (None, None)
+    source_file = None
+
+    def __init__(self, compiler, contexts):
+        self._compiler = compiler
+        self.contexts = contexts
+        contexts['nothing'] = None
+        contexts['default'] = Default
+
+        self.repeat_vars = rv = {}
+        # Wrap this, as it is visible to restricted code
+        contexts['repeat'] = rep =  self._context_class(rv)
+        contexts['loop'] = rep # alias
+
+        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 getCompiler(self):
+        return self._compiler
+
+    def beginScope(self):
+        self._scope_stack.append([self.local_vars.copy()])
+
+    def endScope(self):
+        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.local_vars[name] = value
+
+    def setGlobal(self, name, value):
+        self.global_vars[name] = value
+
+    def setRepeat(self, name, expr):
+        expr = self.evaluate(expr)
+        if not expr:
+            return self._compiler.Iterator(name, (), self)
+        it = self._compiler.Iterator(name, expr, self)
+        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,
+                 isinstance=isinstance, StringType=StringType):
+        if isinstance(expression, StringType):
+            expression = self._compiler.compile(expression)
+        __traceback_supplement__ = (
+            TALESTracebackSupplement, self, expression)
+        v = expression(self)
+        return v
+
+    evaluateValue = evaluate
+    evaluateBoolean = evaluate
+
+    def evaluateText(self, expr, None=None):
+        text = self.evaluate(expr)
+        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 createErrorInfo(self, err, position):
+        return ErrorInfo(err, position)
+
+    def getDefault(self):
+        return Default
+
+    def setSourceFile(self, source_file):
+        self.source_file = source_file
+
+    def setPosition(self, position):
+        self.position = position
+
+
+
+class TALESTracebackSupplement:
+    """Implementation of ITracebackSupplement"""
+    def __init__(self, context, expression):
+        self.context = context
+        self.source_url = context.source_file
+        self.line = context.position[0]
+        self.column = context.position[1]
+        self.expression = repr(expression)
+
+    def getInfo(self, as_html=0):
+        import pprint
+        data = self.context.contexts.copy()
+        s = pprint.pformat(data)
+        if not as_html:
+            return '   - Names:\n      %s' % string.replace(s, '\n', '\n      ')
+        else:
+            from cgi import escape
+            return '<b>Names:</b><pre>%s</pre>' % (escape(s))
+        return None
+
+
+
+class SimpleExpr:
+    '''Simple example of an expression type handler'''
+    def __init__(self, name, expr, engine):
+        self._name = name
+        self._expr = expr
+    def __call__(self, econtext):
+        return self._name, self._expr
+    def __repr__(self):
+        return '<SimpleExpr %s %s>' % (self._name, `self._expr`)
+

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