[Zope-Checkins] CVS: Zope3/lib/python/Zope/PageTemplate - Engine.py:1.2 Expressions.py:1.2 PageTemplate.py:1.2 PageTemplateFile.py:1.2 PythonExpr.py:1.2 SafeMapping.py:1.2 TALES.py:1.2 __init__.py:1.2
Jim Fulton
jim@zope.com
Mon, 10 Jun 2002 19:30:00 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/PageTemplate
In directory cvs.zope.org:/tmp/cvs-serv20468/lib/python/Zope/PageTemplate
Added Files:
Engine.py Expressions.py PageTemplate.py PageTemplateFile.py
PythonExpr.py SafeMapping.py TALES.py __init__.py
Log Message:
Merged Zope-3x-branch into newly forked Zope3 CVS Tree.
=== Zope3/lib/python/Zope/PageTemplate/Engine.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2002 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.
+#
+##############################################################################
+"""Expression engine configuration and registration.
+
+Each expression engine can have its own expression types and base names.
+
+$Id$
+"""
+
+from TALES import ExpressionEngine, RegistrationError
+from Expressions import PathExpr, StringExpr, NotExpr, DeferExpr
+from Expressions import SimpleModuleImporter
+from PythonExpr import PythonExpr
+
+def Engine():
+ e = ExpressionEngine()
+ reg = e.registerType
+ for pt in PathExpr._default_type_names:
+ reg(pt, PathExpr)
+ reg('string', StringExpr)
+ reg('python', PythonExpr)
+ reg('not', NotExpr)
+ reg('defer', DeferExpr)
+ e.registerBaseName('modules', SimpleModuleImporter())
+ return e
+
+Engine = Engine()
+
=== Zope3/lib/python/Zope/PageTemplate/Expressions.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Basic Page Template expression types.
+
+$Id$
+"""
+__metaclass__ = type # All classes are new style when run with Python 2.2+
+
+import re, sys
+
+from TALES import ExpressionEngine, CompilerError, RegistrationError
+from TALES import _valid_name, _parse_expr, NAME_RE, Undefined
+from PythonExpr import PythonExpr
+
+Undefs = (Undefined, AttributeError, KeyError,
+ TypeError, IndexError)
+
+_marker = object()
+
+def simpleTraverse(object, path_items, econtext):
+ """Traverses a sequence of names, first trying attributes then items.
+ """
+
+ for name in path_items:
+ next = getattr(object, name, _marker)
+ if next is not _marker:
+ object = next
+ elif hasattr(object, '__getitem__'):
+ object = object[name]
+ else:
+ raise NameError, name
+ return object
+
+
+class SubPathExpr:
+ def __init__(self, path, traverser):
+ self._path = path = str(path).strip().split('/')
+ self._base = base = path.pop(0)
+ self._traverser = traverser
+ 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)
+ base = self._base
+ if base == 'CONTEXTS': # Special base name
+ ob = econtext.contexts
+ else:
+ ob = vars[base]
+ if isinstance(ob, DeferWrapper):
+ ob = ob()
+ if path:
+ ob = self._traverser(ob, path, econtext)
+ return ob
+
+
+
+class PathExpr:
+ """One or more subpath expressions, separated by '|'.
+ """
+
+ # _default_type_names contains the expression type names this
+ # class is usually registered for.
+ _default_type_names = (
+ 'standard',
+ 'path',
+ 'exists',
+ 'nocall',
+ )
+
+ def __init__(self, name, expr, engine, traverser=simpleTraverse):
+ self._s = expr
+ self._name = name
+ paths = expr.split('|')
+ self._subexprs = []
+ add = self._subexprs.append
+ for i in range(len(paths)):
+ path = paths[i].lstrip()
+ if _parse_expr(path):
+ # This part is the start of another expression type,
+ # so glue it back together and compile it.
+ add(engine.compile('|'.join(paths[i:]).lstrip()))
+ break
+ add(SubPathExpr(path, traverser)._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):
+ 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':
+ return ob
+
+ # Call the object if it is callable.
+ if hasattr(ob, '__call__'):
+ return ob()
+ return ob
+
+ 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 '<PathExpr %s:%s>' % (self._name, `self._s`)
+
+
+
+_interp = re.compile(r'\$(%(n)s)|\${(%(n)s(?:/[^}]*)*)}' % {'n': NAME_RE})
+
+class StringExpr:
+ def __init__(self, name, expr, engine):
+ self._s = expr
+ if '%' in expr:
+ expr = expr.replace('%', '%%')
+ self._vars = vars = []
+ if '$' in expr:
+ # Use whatever expr type is registered as "path".
+ path_type = engine.getTypes()['path']
+ parts = []
+ for exp in expr.split('$$'):
+ if parts: parts.append('$')
+ m = _interp.search(exp)
+ while m is not None:
+ parts.append(exp[:m.start()])
+ parts.append('%s')
+ vars.append(path_type(
+ '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)
+ vvals.append(v)
+ return self._expr % tuple(vvals)
+
+ def __str__(self):
+ return 'string expression (%s)' % `self._s`
+
+ def __repr__(self):
+ return '<StringExpr %s>' % `self._s`
+
+
+class NotExpr:
+ def __init__(self, name, expr, engine):
+ self._s = expr = expr.lstrip()
+ self._c = engine.compile(expr)
+
+ def __call__(self, econtext):
+ return int(not econtext.evaluateBoolean(self._c))
+
+ def __repr__(self):
+ return '<NotExpr %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 = expr.lstrip()
+ self._c = compiler.compile(expr)
+
+ def __call__(self, econtext):
+ return DeferWrapper(self._c, econtext)
+
+ def __repr__(self):
+ return '<DeferExpr %s>' % `self._s`
+
+
+class SimpleModuleImporter:
+ """Minimal module importer with no security."""
+ def __getitem__(self, module):
+ mod = __import__(module)
+ path = module.split('.')
+ for name in path[1:]:
+ mod = getattr(mod, name)
+ return mod
+
=== Zope3/lib/python/Zope/PageTemplate/PageTemplate.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+
+$Id$
+"""
+__metaclass__ = type # All classes are new style when run with Python 2.2+
+
+import sys
+from Zope.TAL.TALParser import TALParser
+from Zope.TAL.HTMLTALParser import HTMLTALParser
+from Zope.TAL.TALGenerator import TALGenerator
+from Zope.TAL.TALInterpreter import TALInterpreter
+from Engine import Engine
+from cStringIO import StringIO
+
+
+class MacroCollection:
+ def __get__(self, parent, type=None):
+ parent._cook_check()
+ return parent._v_macros
+
+
+_default_options = {}
+
+class PageTemplate:
+ """Page Templates using TAL, TALES, and METAL.
+
+ Subclassing
+ -----------
+
+ The following methods have certain internal responsibilities.
+
+ pt_getContext(**keywords)
+ Should ignore keyword arguments that it doesn't care about,
+ and construct the namespace passed to the TALES expression
+ engine. This method is free to use the keyword arguments it
+ receives.
+
+ pt_render(namespace, source=0)
+ Responsible the TAL interpreter to perform the rendering. The
+ namespace argument is a mapping which defines the top-level
+ namespaces passed to the TALES expression engine.
+
+ __call__(*args, **keywords)
+ Calls pt_getContext() to construct the top-level namespace
+ passed to the TALES expression engine, then calls pt_render()
+ to perform the rendering.
+ """
+
+ content_type = 'text/html'
+ expand = 1
+ _v_errors = ()
+ _v_warnings = ()
+ _v_program = None
+ _v_macros = None
+ _v_cooked = 0
+ _text = ''
+ _engine_name = 'default'
+ _error_start = '<!-- Page Template Diagnostics'
+
+ macros = MacroCollection()
+
+ def pt_edit(self, text, content_type):
+ if content_type:
+ self.content_type = str(content_type)
+ if hasattr(text, 'read'):
+ text = text.read()
+ self.write(text)
+
+ def pt_getContext(self, args=(), options=_default_options, **ignored):
+ rval = {'template': self,
+ 'options': options,
+ 'args': args,
+ 'nothing': None,
+ }
+ rval.update(self.pt_getEngine().getBaseNames())
+ return rval
+
+ def __call__(self, *args, **kwargs):
+ return self.pt_render(self.pt_getContext(args, kwargs))
+
+ pt_getEngineContext = Engine.getContext
+
+ def pt_getEngine(self):
+ return Engine
+
+ def pt_render(self, namespace, source=0):
+ """Render this Page Template"""
+ self._cook_check()
+ __traceback_supplement__ = (PageTemplateTracebackSupplement,
+ self, namespace)
+ if self._v_errors:
+ raise PTRuntimeError(str(self._v_errors))
+ output = StringIO()
+
+ context = self.pt_getEngineContext(namespace)
+ TALInterpreter(self._v_program, self._v_macros,
+ context, output, tal=not source, strictinsert=0)()
+ return output.getvalue()
+
+ def pt_errors(self, namespace):
+ self._cook_check()
+ err = self._v_errors
+ if err:
+ return err
+ try:
+ self.pt_render(namespace, source=1)
+ except:
+ return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
+
+ def pt_warnings(self):
+ self._cook_check()
+ return self._v_warnings
+
+ def write(self, text):
+ assert isinstance(text, str)
+ if text.startswith(self._error_start):
+ errend = text.find('-->')
+ if errend >= 0:
+ text = text[errend + 4:]
+ if self._text != text:
+ self._text = text
+ self._cook()
+
+ def read(self):
+ """Gets the source, sometimes with macros expanded."""
+ self._cook_check()
+ if not self._v_errors:
+ if not self.expand:
+ return self._text
+ try:
+ # XXX not clear how this ever gets called, but the
+ # first arg to pt_render() needs to change if it ever does.
+ 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,
+ '\n'.join(self._v_errors),
+ self._text))
+
+ def pt_source_file(self):
+ """To be overridden."""
+ return None
+
+ def _cook_check(self):
+ if not self._v_cooked:
+ self._cook()
+
+ def _cook(self):
+ """Compile the TAL and METAL statments.
+
+ Cooking must not fail due to compilation errors in templates.
+ """
+ engine = self.pt_getEngine()
+ source_file = self.pt_source_file()
+ if self.html():
+ gen = TALGenerator(engine, xml=0, source_file=source_file)
+ parser = HTMLTALParser(gen)
+ else:
+ gen = TALGenerator(engine, source_file=source_file)
+ parser = TALParser(gen)
+
+ self._v_errors = ()
+ try:
+ parser.parseString(self._text)
+ self._v_program, self._v_macros = parser.getCode()
+ self._v_macros = self._v_macros
+ 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(self, 'is_html'):
+ return self.content_type == 'text/html'
+ return self.is_html
+
+
+
+class PTRuntimeError(RuntimeError):
+ '''The Page Template has template errors that prevent it from rendering.'''
+ pass
+
+
+class PageTemplateTracebackSupplement:
+ #__implements__ = ITracebackSupplement
+
+ def __init__(self, pt, namespace):
+ self.manageable_object = pt
+ try:
+ w = pt.pt_warnings()
+ except: # We're already trying to report an error, don't make another.
+ w = ()
+ e = pt.pt_errors(namespace)
+ if e:
+ w = list(w) + list(e)
+ self.warnings = w
=== Zope3/lib/python/Zope/PageTemplate/PageTemplateFile.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Filesystem Page Template module
+
+Zope object encapsulating a Page Template from the filesystem.
+"""
+
+__metatype__ = type
+
+__version__ = '$Revision$'[11:-2]
+
+import os, sys
+from zLOG import LOG, ERROR, INFO
+from PageTemplate import PageTemplate
+
+def package_home(gdict):
+ filename = gdict["__file__"]
+ return os.path.dirname(filename)
+
+class PageTemplateFile(PageTemplate):
+ "Zope wrapper for filesystem Page Template using TAL, TALES, and METAL"
+
+ _v_last_read = 0
+
+ def __init__(self, filename, _prefix=None):
+ if not isinstance(_prefix, str):
+ if _prefix is None:
+ _prefix = sys._getframe(1).f_globals
+ _prefix = package_home(_prefix)
+
+ self.filename = os.path.join(_prefix, filename)
+
+ def _cook_check(self):
+ if self._v_last_read and not __debug__:
+ return
+ __traceback_info__ = self.filename
+ try:
+ mtime = os.path.getmtime(self.filename)
+ except OSError:
+ mtime = 0
+ if getattr(self, '_v_program', 0) and mtime == self._v_last_read:
+ return
+ self.pt_edit(open(self.filename), None)
+ self._cook()
+ if self._v_errors:
+ LOG('PageTemplateFile', ERROR, 'Error in template',
+ '\n'.join(self._v_errors))
+ return
+ self._v_last_read = mtime
+
+ def document_src(self, REQUEST=None):
+ """Return expanded document source."""
+
+ if REQUEST is not None:
+ REQUEST.getResponse().setHeader('Content-Type', self.content_type)
+ return self.read()
+
+ def pt_source_file(self):
+ return self.filename
+
+ def __getstate__(self):
+ raise TypeError("non-picklable object")
=== Zope3/lib/python/Zope/PageTemplate/PythonExpr.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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$'[11:-2]
+
+class PythonExpr:
+ def __init__(self, name, expr, engine):
+ text = expr.replace('\n', ' ').strip()
+ self.text = text
+ # The next line can legally raise SyntaxError.
+ self._code = code = compile(text, '<string>', 'eval')
+ self._varnames = code.co_names
+
+ def _bind_used_names(self, econtext, builtins):
+ # Bind template variables
+ names = {}
+ vars = econtext.vars
+ marker = self
+ for vname in self._varnames:
+ val = vars.get(vname, marker)
+ if val is not marker:
+ names[vname] = val
+ elif vname not in builtins:
+ # Fall back to using expression types as variable values.
+ val = econtext._engine.getTypes().get(vname, marker)
+ if val is not marker:
+ val = ExprTypeProxy(vname, val, econtext)
+ names[vname] = val
+
+ names['__builtins__'] = builtins
+ return names
+
+ def __call__(self, econtext):
+ __traceback_info__ = self.text
+ vars = self._bind_used_names(econtext, __builtins__)
+ return eval(self._code, vars)
+
+ def __str__(self):
+ return 'Python expression "%s"' % self.text
+
+ def __repr__(self):
+ return '<PythonExpr %s>' % self.text
+
+
+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)
=== Zope3/lib/python/Zope/PageTemplate/SafeMapping.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Simple variation of MultiMapping used to support layers of variable
+declarations in TAL."""
+
+
+class SafeMapping:
+ def __init__(self, *dicts):
+ self._mappings = list(dicts)
+ self._mappings.reverse()
+
+ def __getitem__(self, key):
+ for d in self._mappings:
+ if key in d:
+ return d[key]
+ raise KeyError, key
+
+ def __contains__(self, key):
+ for d in self._mappings:
+ if key in d:
+ return 1
+ return 0
+
+ has_key = __contains__
+
+ def get(self, key, default=None):
+ for d in self._mappings:
+ if key in d:
+ return d[key]
+ return default
+
+ def _push(self, dict):
+ self._mappings.insert(0, dict)
+
+ def _pop(self, count=1):
+ del self._mappings[:count]
=== Zope3/lib/python/Zope/PageTemplate/TALES.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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
+"""
+__metaclass__ = type # All classes are new style when run with Python 2.2+
+
+__version__ = '$Revision$'[11:-2]
+
+import re
+import sys
+from types import StringType
+
+import Zope.ZTUtils
+from SafeMapping import SafeMapping
+
+
+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 evaluation"""
+
+class Undefined(TALESError):
+ '''Exception raised on traversal of an undefined path'''
+
+class CompilerError(Exception):
+ '''TALES Compiler Error'''
+
+class RegistrationError(Exception):
+ '''Expression type or base name registration Error'''
+
+
+_default = object()
+
+_marker = object()
+
+
+class Iterator(Zope.ZTUtils.Iterator):
+ def __init__(self, name, seq, context):
+ Zope.ZTUtils.Iterator.__init__(self, seq)
+ self.name = name
+ self._context = context
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ if Zope.ZTUtils.Iterator.next(self):
+ self._context.setLocal(self.name, self.seq[self.index])
+ return 1
+ return 0
+
+
+
+class ErrorInfo:
+ """Information about an exception passed to an on-error handler."""
+ 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 ExpressionEngine:
+ '''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.
+ '''
+ def __init__(self):
+ self.types = {}
+ self.base_names = {}
+ self.iteratorFactory = Iterator
+
+ def registerType(self, name, handler):
+ if not _valid_name(name):
+ raise RegistrationError, (
+ 'Invalid expression type name "%s".' % name)
+ types = self.types
+ if name in types:
+ raise RegistrationError, (
+ 'Multiple registrations for Expression type "%s".' %
+ name)
+ types[name] = handler
+
+ def getTypes(self):
+ return self.types
+
+ def registerBaseName(self, name, object):
+ if not _valid_name(name):
+ raise RegistrationError, 'Invalid base name "%s".' % name
+ base_names = self.base_names
+ if name in base_names:
+ raise RegistrationError, (
+ 'Multiple registrations for base name "%s".' % name)
+ base_names[name] = object
+
+ def getBaseNames(self):
+ return self.base_names
+
+ 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.iteratorFactory(name, (), self)
+ it = self._engine.iteratorFactory(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):
+ 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 Zope.Exceptions.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' % s.replace('\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`)
+
=== Zope3/lib/python/Zope/PageTemplate/__init__.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 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 Templates for Zope 3."""