From 1fa624281c07a94021df91011d20f67f4115cf60 Mon Sep 17 00:00:00 2001 From: richard Date: Fri, 30 Aug 2002 08:27:34 +0000 Subject: [PATCH] Adding PageTemplates to the dist git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1001 57a73879-2fb5-44c3-a270-3262357dd7e2 --- PageTemplates/Expressions.py | 375 ++++++++++++++++++++++++++++++++++ PageTemplates/PageTemplate.py | 207 +++++++++++++++++++ PageTemplates/PathIterator.py | 48 +++++ PageTemplates/PythonExpr.py | 82 ++++++++ PageTemplates/README.txt | 7 + PageTemplates/TALES.py | 285 ++++++++++++++++++++++++++ PageTemplates/__init__.py | 28 +++ 7 files changed, 1032 insertions(+) create mode 100644 PageTemplates/Expressions.py create mode 100755 PageTemplates/PageTemplate.py create mode 100644 PageTemplates/PathIterator.py create mode 100644 PageTemplates/PythonExpr.py create mode 100644 PageTemplates/README.txt create mode 100644 PageTemplates/TALES.py create mode 100644 PageTemplates/__init__.py diff --git a/PageTemplates/Expressions.py b/PageTemplates/Expressions.py new file mode 100644 index 0000000..b2adad2 --- /dev/null +++ b/PageTemplates/Expressions.py @@ -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 + diff --git a/PageTemplates/PageTemplate.py b/PageTemplates/PageTemplate.py new file mode 100755 index 0000000..d97b2a5 --- /dev/null +++ b/PageTemplates/PageTemplate.py @@ -0,0 +1,207 @@ +############################################################################## +# +# 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 module + +HTML- and XML-based template objects using TAL, TALES, and METAL. +""" + +__version__='$Revision: 1.1 $'[11:-2] + +import sys + +from TAL.TALParser import TALParser +from TAL.HTMLTALParser import HTMLTALParser +from TAL.TALGenerator import TALGenerator +from TAL.TALInterpreter import TALInterpreter +from Expressions import getEngine +from string import join, strip, rstrip, split, replace, lower, find +from cStringIO import StringIO +from ExtensionClass import Base +from ComputedAttribute import ComputedAttribute + + +class PageTemplate(Base): + "Page Templates using TAL, TALES, and METAL" + + content_type = 'text/html' + expand = 0 + _v_errors = () + _v_warnings = () + _v_program = None + _v_macros = None + _v_cooked = 0 + id = '(unknown)' + _text = '' + _error_start = '') + if errend >= 0: + text = text[errend + 4:] + if self._text != text: + self._text = text + self._cook() + + def read(self): + if not self._v_cooked: + self._cook() + if not self._v_errors: + if not self.expand: + return self._text + try: + return self.pt_render(source=1) + except: + return ('%s\n Macro expansion failed\n %s\n-->\n%s' % + (self._error_start, "%s: %s" % sys.exc_info()[:2], + self._text) ) + + return ('%s\n %s\n-->\n%s' % (self._error_start, + join(self._v_errors, '\n '), + self._text)) + + def _cook(self): + """Compile the TAL and METAL statments. + + Cooking must not fail due to compilation errors in templates. + """ + source_file = self.pt_source_file() + if self.html(): + gen = TALGenerator(getEngine(), xml=0, source_file=source_file) + parser = HTMLTALParser(gen) + else: + gen = TALGenerator(getEngine(), source_file=source_file) + parser = TALParser(gen) + + self._v_errors = () + try: + parser.parseString(self._text) + self._v_program, self._v_macros = parser.getCode() + except: + self._v_errors = ["Compilation failed", + "%s: %s" % sys.exc_info()[:2]] + self._v_warnings = parser.getWarnings() + self._v_cooked = 1 + + def html(self): + if not hasattr(getattr(self, 'aq_base', self), 'is_html'): + return self.content_type == 'text/html' + return self.is_html + +class _ModuleImporter: + def __getitem__(self, module): + mod = __import__(module) + path = split(module, '.') + for name in path[1:]: + mod = getattr(mod, name) + return mod + +ModuleImporter = _ModuleImporter() + +class PTRuntimeError(RuntimeError): + '''The Page Template has template errors that prevent it from rendering.''' + pass + + +class PageTemplateTracebackSupplement: + #__implements__ = ITracebackSupplement + + def __init__(self, pt): + self.object = pt + w = pt.pt_warnings() + e = pt.pt_errors() + if e: + w = list(w) + list(e) + self.warnings = w + diff --git a/PageTemplates/PathIterator.py b/PageTemplates/PathIterator.py new file mode 100644 index 0000000..ded1d29 --- /dev/null +++ b/PageTemplates/PathIterator.py @@ -0,0 +1,48 @@ +############################################################################## +# +# 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 +from string import split + +class Iterator(TALES.Iterator): + def __bobo_traverse__(self, REQUEST, name): + if name in ('first', 'last'): + path = REQUEST['TraversalRequestNameStack'] + names = list(path) + names.reverse() + path[:] = [tuple(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 + diff --git a/PageTemplates/PythonExpr.py b/PageTemplates/PythonExpr.py new file mode 100644 index 0000000..afa40d2 --- /dev/null +++ b/PageTemplates/PythonExpr.py @@ -0,0 +1,82 @@ +############################################################################## +# +# 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 +# +############################################################################## + +"""Generic Python Expression Handler +""" + +__version__='$Revision: 1.1 $'[11:-2] + +from TALES import CompilerError +from string import strip, split, join, replace, lstrip +from sys import exc_info + +class getSecurityManager: + '''Null security manager''' + def validate(self, *args, **kwargs): + return 1 + addContext = removeContext = validateValue = validate + +class PythonExpr: + def __init__(self, name, expr, engine): + self.expr = expr = replace(strip(expr), '\n', ' ') + try: + d = {} + exec 'def f():\n return %s\n' % strip(expr) in d + self._f = d['f'] + except: + raise CompilerError, ('Python expression error:\n' + '%s: %s') % exc_info()[:2] + self._get_used_names() + + def _get_used_names(self): + self._f_varnames = vnames = [] + for vname in self._f.func_code.co_names: + if vname[0] not in '$_': + vnames.append(vname) + + def _bind_used_names(self, econtext): + # Bind template variables + names = {} + vars = econtext.vars + getType = econtext._engine.getTypes().get + for vname in self._f_varnames: + has, val = vars.has_get(vname) + if not has: + has = val = getType(vname) + if has: + val = ExprTypeProxy(vname, val, econtext) + if has: + names[vname] = val + return names + + def __call__(self, econtext): + __traceback_info__ = self.expr + f = self._f + f.func_globals.update(self._bind_used_names(econtext)) + return f() + + def __str__(self): + return 'Python expression "%s"' % self.expr + def __repr__(self): + return '' % self.expr + +class ExprTypeProxy: + '''Class that proxies access to an expression type handler''' + def __init__(self, name, handler, econtext): + self._name = name + self._handler = handler + self._econtext = econtext + def __call__(self, text): + return self._handler(self._name, text, + self._econtext._engine)(self._econtext) + diff --git a/PageTemplates/README.txt b/PageTemplates/README.txt new file mode 100644 index 0000000..013f0ae --- /dev/null +++ b/PageTemplates/README.txt @@ -0,0 +1,7 @@ +See the +ZPT project Wiki for more information about Page Templates, or +the download page +for installation instructions and the most recent version of the software. + +This Product requires the TAL and ZTUtils packages to be installed in +your Python path (not Products). See the links above for more information. diff --git a/PageTemplates/TALES.py b/PageTemplates/TALES.py new file mode 100644 index 0000000..089ccbc --- /dev/null +++ b/PageTemplates/TALES.py @@ -0,0 +1,285 @@ +############################################################################## +# +# 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 +""" + +__version__='$Revision: 1.1 $'[11:-2] + +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 + +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, engine, contexts): + self._engine = engine + 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 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._engine.Iterator(name, (), self) + it = self._engine.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._engine.compile(expression) + __traceback_supplement__ = ( + TALESTracebackSupplement, self, expression) + v = expression(self) + return v + + evaluateValue = evaluate + + def evaluateBoolean(self, expr): + return not not self.evaluate(expr) + + 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 'Names:
%s
' % (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 '' % (self._name, `self._expr`) + diff --git a/PageTemplates/__init__.py b/PageTemplates/__init__.py new file mode 100644 index 0000000..e1997a6 --- /dev/null +++ b/PageTemplates/__init__.py @@ -0,0 +1,28 @@ +############################################################################## +# +# 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 +# +############################################################################## +__doc__='''Package wrapper for Page Templates + +This wrapper allows the Page Template modules to be segregated in a +separate package. + +$Id: __init__.py,v 1.1 2002-08-30 08:27:34 richard Exp $''' +__version__='$$'[11:-2] + + +# Placeholder for Zope Product data +misc_ = {} + +def initialize(context): + # Import lazily, and defer initialization to the module + import ZopePageTemplate + ZopePageTemplate.initialize(context) -- 2.30.2