[Zope3-checkins] CVS: Zope3/src/zope/pagetemplate - __init__.py:1.2 engine.py:1.2 expressions.py:1.2 interfaces.py:1.2 iterator.py:1.2 pagetemplate.py:1.2 pagetemplatefile.py:1.2 pythonexpr.py:1.2 readme.txt:1.2 safemapping.py:1.2 tales.py:1.2
Jim Fulton
jim@zope.com
Wed, 25 Dec 2002 09:15:44 -0500
Update of /cvs-repository/Zope3/src/zope/pagetemplate
In directory cvs.zope.org:/tmp/cvs-serv20790/src/zope/pagetemplate
Added Files:
__init__.py engine.py expressions.py interfaces.py iterator.py
pagetemplate.py pagetemplatefile.py pythonexpr.py readme.txt
safemapping.py tales.py
Log Message:
Grand renaming:
- Renamed most files (especially python modules) to lower case.
- Moved views and interfaces into separate hierarchies within each
project, where each top-level directory under the zope package
is a separate project.
- Moved everything to src from lib/python.
lib/python will eventually go away. I need access to the cvs
repository to make this happen, however.
There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.
=== Zope3/src/zope/pagetemplate/__init__.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/__init__.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Zope 3 compatible ZTUtils."""
+
+from zope.pagetemplate.iterator import Iterator
=== Zope3/src/zope/pagetemplate/engine.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/engine.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# 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 zope.pagetemplate.tales import ExpressionEngine, RegistrationError
+from zope.pagetemplate.expressions import PathExpr, StringExpr, NotExpr, DeferExpr
+from zope.pagetemplate.expressions import SimpleModuleImporter
+from zope.pagetemplate.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/src/zope/pagetemplate/expressions.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/expressions.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,247 @@
+##############################################################################
+#
+# 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 types import StringTypes
+
+from zope.pagetemplate.tales import ExpressionEngine, CompilerError, RegistrationError
+from zope.pagetemplate.tales import _valid_name, _parse_expr, NAME_RE, Undefined
+from zope.pagetemplate.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):
+ 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, StringTypes):
+ 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/src/zope/pagetemplate/interfaces.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/interfaces.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Interface that describes the 'macros' attribute of a PageTemplate.
+
+$Id$
+"""
+from zope.interface import Interface, Attribute
+
+class IMacrosAttribute(Interface):
+
+ macros = Attribute("An object that implements the __getitem__ "
+ "protocol, containing page template macros.")
=== Zope3/src/zope/pagetemplate/iterator.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/iterator.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,80 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+__doc__='''Iterator class
+
+$Id$'''
+__version__='$Revision$'[11:-2]
+
+class Iterator:
+ '''Simple Iterator class'''
+
+ __allow_access_to_unprotected_subobjects__ = 1
+
+ def __init__(self, seq):
+ self.seq = seq
+ self.nextIndex = 0
+
+ def next(self):
+ i = self.nextIndex
+ try:
+ self.seq[i]
+ except IndexError:
+ return 0
+ self.index = i
+ self.nextIndex = i+1
+ return 1
+
+ def number(self): return self.nextIndex
+
+ def even(self): return not self.index % 2
+
+ def odd(self): return self.index % 2
+
+ def letter(self, base=ord('a'), radix=26):
+ index = self.index
+ s = ''
+ while 1:
+ index, off = divmod(index, radix)
+ s = chr(base + off) + s
+ if not index: return s
+
+ def Letter(self):
+ return self.letter(base=ord('A'))
+
+ def Roman(self, rnvalues=(
+ (1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
+ (100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
+ (10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')) ):
+ n = self.index + 1
+ s = ''
+ for v, r in rnvalues:
+ rct, n = divmod(n, v)
+ s = s + r * rct
+ return s
+
+ def roman(self):
+ return self.Roman.lower()
+
+ def start(self): return self.nextIndex == 1
+
+ def end(self):
+ try: self.seq[self.nextIndex]
+ except IndexError: return 1
+ return 0
+
+ def item(self):
+ return self.seq[self.index]
+
+ def length(self):
+ return len(self.seq)
=== Zope3/src/zope/pagetemplate/pagetemplate.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/pagetemplate.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,221 @@
+##############################################################################
+#
+# 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 zope.pagetemplate.engine import Engine
+from zope.pagetemplate.interfaces import IMacrosAttribute
+# Don't use cStringIO here! It's not unicode aware.
+from StringIO 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.
+ """
+
+ # XXX this breaks something in the Zope3 views registries.
+ # Temporarily removed. SteveA 2002-10-22
+ #__implements__ = IMacrosAttribute
+
+ 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(u'')
+
+ 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
+ # XXX can this be done only if we changed self._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()
+ 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/src/zope/pagetemplate/pagetemplatefile.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/pagetemplatefile.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# 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.
+"""
+
+__metaclass__ = type
+
+__version__ = '$Revision$'[11:-2]
+
+import os, sys
+import logging
+
+from zope.pagetemplate.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 self._v_program is not None and mtime == self._v_last_read:
+ return
+ self.pt_edit(open(self.filename), None)
+ self._cook()
+ if self._v_errors:
+ logging.error('PageTemplateFile: Error in template: %s',
+ '\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.response.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/src/zope/pagetemplate/pythonexpr.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/pythonexpr.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# 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/src/zope/pagetemplate/readme.txt 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/readme.txt Wed Dec 25 09:15:13 2002
@@ -0,0 +1,92 @@
+Page Templates
+
+ Introduction
+
+ Page Templates provide an elegant templating mechanism that
+ achieves a clean separation of presentation and application
+ logic while allowing for designers to work with templates
+ in their visual editing tools (FrontPage, Dreamweaver, GoLive,
+ etc.)
+
+ This document focuses on usage of Page Templates outside of
+ a Zope context, it does *not* explain how to write page templates
+ as there are several resources on the web which do so.
+
+ Dependencies
+
+ Zope3 Package Dependencies
+
+ - TAL (Template Attribute Language)
+
+ - Interface
+
+ - ZTUtils (batching utilities for zpt)
+
+ - The standard logging package ("logging") from Python 2.3.
+
+ Simple Usage
+
+ Using PageTemplates outside of Zope3 is very easy and straight
+ forward. a quick example::
+
+ > python
+ >> from Zope.PageTemplate.PageTemplateFile import PageTemplateFile
+ >> my_pt = PageTemplateFile('hello_world.pt')
+ >> my_pt()
+ u'<html><body>Hello World</body></html>'
+
+ Setting Up Contexts
+
+ Rendering a page template without binding data to is not very
+ interesting. By default keyword arguments you pass in page
+ templates appear in the options namespace.
+
+ 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.
+
+ Narrative ()
+
+ Narrative (Subclassing PageTemplates)
+
+ Lets say we want to alter page templates such that keyword
+ arguments appear as top level items in the namespace. we can
+ subclass page template and alter the default behavior of
+ pt_getContext to add them in::
+
+ from Zope.PageTemplate.PageTemplate import PageTemplate
+
+ class mypt(PageTemplate):
+ def pt_getContext(self, args=(), options={}, **kw):
+ rval = PageTemplate.pt_getContext(self, args=args)
+ options.update(rval)
+ return options
+
+ class foo:
+ def getContents(self): return 'hi'
+
+ So now we can bind objects in a more arbitrary fashion, like
+ the following::
+
+ template = """
+ <html>
+ <body>
+ <b tal:replace="das_object/getContents">Good Stuff Here</b>
+ </body>
+ </html>
+ """
+
+ pt = mypt()
+ pt.write(template)
+ pt(das_object=foo())
+
+ Author
+
+ Kapil Thangavelu <hazmat at objectrealms.net>
=== Zope3/src/zope/pagetemplate/safemapping.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/safemapping.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# 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/src/zope/pagetemplate/tales.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:15:44 2002
+++ Zope3/src/zope/pagetemplate/tales.py Wed Dec 25 09:15:13 2002
@@ -0,0 +1,299 @@
+##############################################################################
+#
+# 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 StringTypes
+
+from zope.pagetemplate import iterator
+from zope.pagetemplate import safemapping
+
+from zope.tal.interfaces import ITALESCompiler, ITALESEngine, ITALESErrorInfo
+
+
+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(iterator.Iterator):
+ def __init__(self, name, seq, context):
+ iterator.Iterator.__init__(self, seq)
+ self.name = name
+ self._context = context
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ if iterator.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."""
+
+ __implements__ = ITALESErrorInfo
+
+ 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.
+ '''
+
+ __implements__ = ITALESCompiler
+
+ 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.
+ '''
+
+ __implements__ = ITALESEngine
+
+ _context_class = safemapping.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):
+ if isinstance(expression, str):
+ expression = self._engine.compile(expression)
+ __traceback_supplement__ = (
+ TALESTracebackSupplement, self, expression)
+ return expression(self)
+
+ 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
+ if not isinstance(text, StringTypes):
+ text = unicode(text)
+ return 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()
+ if 'modules' in data:
+ del data['modules'] # the list is really long and boring
+ 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`)