[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`)