changeset 2349:b43efe461b3e

update PageTemplates to latest Zope codebase
author Richard Jones <richard@users.sourceforge.net>
date Fri, 21 May 2004 05:56:46 +0000
parents 8c2402a78bb0
children 46d85c9622bb
files roundup/cgi/PageTemplates/ComputedAttribute.py roundup/cgi/PageTemplates/Expressions.py roundup/cgi/PageTemplates/PageTemplate.py roundup/cgi/PageTemplates/PathIterator.py roundup/cgi/PageTemplates/PythonExpr.py roundup/cgi/PageTemplates/TALES.py roundup/cgi/PageTemplates/__init__.py
diffstat 7 files changed, 144 insertions(+), 152 deletions(-) [+]
line wrap: on
line diff
--- a/roundup/cgi/PageTemplates/ComputedAttribute.py	Fri May 21 05:36:30 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-class ComputedAttribute:
-    def __init__(self, callable, level):
-        self.callable = callable
-        self.level = level
-    def __of__(self, *args):
-        if self.level > 0:
-            return self.callable
-        if isinstance(self.callable, type('')):
-            return getattr(args[0], self.callable)
-        return self.callable(*args)
-
--- a/roundup/cgi/PageTemplates/Expressions.py	Fri May 21 05:36:30 2004 +0000
+++ b/roundup/cgi/PageTemplates/Expressions.py	Fri May 21 05:56:46 2004 +0000
@@ -1,37 +1,32 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+# Modified for Roundup:
 # 
-##############################################################################
+# 1. Removed all Zope-specific code (doesn't even try to import that stuff now)
+# 2. Removed all Acquisition
 
 """Page Template Expression Engine
 
 Page Template-specific implementation of TALES, with handlers
 for Python expressions, string literals, and paths.
-
-
-Modified for Roundup 0.5 release:
+"""
 
-- Removed all Zope-specific code (doesn't even try to import that stuff now)
-- Removed all Acquisition
-- Made traceback info more informative
-
-"""
-__docformat__ = 'restructuredtext'
-
-__version__='$Revision: 1.9 $'[11:-2]
+__version__='$Revision: 1.10 $'[11:-2]
 
 import re, sys
 from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
      Undefined, Default, _parse_expr
-from string import strip, split, join, replace, lstrip
+
 
 _engine = None
 def getEngine():
@@ -53,10 +48,17 @@
     reg('defer', DeferExpr)
 
 from PythonExpr import getSecurityManager, PythonExpr
+guarded_getattr = getattr
 try:
     from zExceptions import Unauthorized
 except ImportError:
     Unauthorized = "Unauthorized"
+
+def acquisition_security_filter(orig, inst, name, v, real_validate):
+    if real_validate(orig, inst, name, v):
+        return 1
+    raise Unauthorized, name
+
 def call_with_ns(f, ns, arg=1):
     if arg==2:
         return f(None, ns)
@@ -70,6 +72,8 @@
         __import__(module)
         return sys.modules[module]
 
+SecureModuleImporter = _SecureModuleImporter()
+
 Undefs = (Undefined, AttributeError, KeyError,
           TypeError, IndexError, Unauthorized)
 
@@ -95,9 +99,9 @@
 
 class SubPathExpr:
     def __init__(self, path):
-        self._path = path = split(strip(path), '/')
+        self._path = path = path.strip().split('/')
         self._base = base = path.pop(0)
-        if not _valid_name(base):
+        if base and not _valid_name(base):
             raise CompilerError, 'Invalid variable name "%s"' % base
         # Parse path
         self._dp = dp = []
@@ -123,7 +127,7 @@
                     path[i:i+1] = list(val)
         base = self._base
         __traceback_info__ = 'path expression "%s"'%('/'.join(self._path))
-        if base == 'CONTEXTS':
+        if base == 'CONTEXTS' or not base:
             ob = econtext.contexts
         else:
             ob = vars[base]
@@ -138,15 +142,15 @@
         self._s = expr
         self._name = name
         self._hybrid = 0
-        paths = split(expr, '|')
+        paths = expr.split('|')
         self._subexprs = []
         add = self._subexprs.append
         for i in range(len(paths)):
-            path = lstrip(paths[i])
+            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(lstrip(join(paths[i:], '|'))))
+                add(engine.compile(('|'.join(paths[i:]).lstrip())))
                 self._hybrid = 1
                 break
             add(SubPathExpr(path)._eval)
@@ -194,18 +198,18 @@
     def __repr__(self):
         return '%s:%s' % (self._name, `self._s`)
 
-            
-_interp = re.compile(r'\$(%(n)s)|\${(%(n)s(?:/%(n)s)*)}' % {'n': NAME_RE})
+
+_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 = replace(expr, '%', '%%')
+            expr = expr.replace('%', '%%')
         self._vars = vars = []
         if '$' in expr:
             parts = []
-            for exp in split(expr, '$$'):
+            for exp in expr.split('$$'):
                 if parts: parts.append('$')
                 m = _interp.search(exp)
                 while m is not None:
@@ -219,15 +223,16 @@
                     raise CompilerError, (
                         '$ must be doubled or followed by a simple path')
                 parts.append(exp)
-            expr = join(parts, '')
+            expr = ''.join(parts)
         self._expr = expr
-        
+
     def __call__(self, econtext):
         vvals = []
         for var in self._vars:
             v = var(econtext)
-            if isinstance(v, Exception):
-                raise v
+            # I hope this isn't in use anymore.
+            ## if isinstance(v, Exception):
+            ##     raise v
             vvals.append(v)
         return self._expr % tuple(vvals)
 
@@ -239,11 +244,14 @@
 
 class NotExpr:
     def __init__(self, name, expr, compiler):
-        self._s = expr = lstrip(expr)
+        self._s = expr = expr.lstrip()
         self._c = compiler.compile(expr)
-        
+
     def __call__(self, econtext):
-        return not econtext.evaluateBoolean(self._c)
+        # We use the (not x) and 1 or 0 formulation to avoid changing
+        # the representation of the result in Python 2.3, where the
+        # result of "not" becomes an instance of bool.
+        return (not econtext.evaluateBoolean(self._c)) and 1 or 0
 
     def __repr__(self):
         return 'not:%s' % `self._s`
@@ -261,53 +269,45 @@
 
 class DeferExpr:
     def __init__(self, name, expr, compiler):
-        self._s = expr = lstrip(expr)
+        self._s = expr = expr.lstrip()
         self._c = compiler.compile(expr)
-        
+
     def __call__(self, econtext):
         return DeferWrapper(self._c, econtext)
 
     def __repr__(self):
         return 'defer:%s' % `self._s`
 
-class TraversalError:
-    def __init__(self, path, name):
-        self.path = path
-        self.name = name
 
-def restrictedTraverse(self, path, securityManager,
+def restrictedTraverse(object, path, securityManager,
                        get=getattr, has=hasattr, N=None, M=[],
                        TupleType=type(()) ):
 
     REQUEST = {'path': path}
     REQUEST['TraversalRequestNameStack'] = path = path[:] # Copy!
-    if not path[0]:
-        # If the path starts with an empty string, go to the root first.
-        self = self.getPhysicalRoot()
-        path.pop(0)
-
     path.reverse()
-    object = self
-    #print 'TRAVERSE', (object, path)
-    done = []
+    validate = securityManager.validate
+    __traceback_info__ = REQUEST
     while path:
         name = path.pop()
-        __traceback_info__ = TraversalError(done, name)
+
+        if isinstance(name, TupleType):
+            object = object(*name)
+            continue
 
-#        if isinstance(name, TupleType):
-#            object = apply(object, name)
-#            continue
-
-#        if name[0] == '_':
-#            # Never allowed in a URL.
-#            raise AttributeError, name
+        if not name:
+            # Skip directly to item access
+            o = object[name]
+            # Check access to the item.
+            if not validate(object, object, name, o):
+                raise Unauthorized, name
+            object = o
+            continue
 
         # Try an attribute.
-        o = get(object, name, M)
-#       print '...', (object, name, M, o)
+        o = guarded_getattr(object, name, M)
         if o is M:
             # Try an item.
-#           print '... try an item'
             try:
                 # XXX maybe in Python 2.2 we can just check whether
                 # the object has the attribute "__getitem__"
@@ -319,18 +319,15 @@
                     # Try to re-raise the original attribute error.
                     # XXX I think this only happens with
                     # ExtensionClass instances.
-                    get(object, name)
+                    guarded_getattr(object, name)
                 raise
             except TypeError, exc:
                 if str(exc).find('unsubscriptable') >= 0:
                     # The object does not support the item interface.
                     # Try to re-raise the original attribute error.
                     # XXX This is sooooo ugly.
-                    get(object, name)
+                    guarded_getattr(object, name)
                 raise
-        #print '... object is now', `o`
         object = o
-        done.append((name, o))
 
     return object
-
--- a/roundup/cgi/PageTemplates/PageTemplate.py	Fri May 21 05:36:30 2004 +0000
+++ b/roundup/cgi/PageTemplates/PageTemplate.py	Fri May 21 05:56:46 2004 +0000
@@ -1,42 +1,40 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+# Modified for Roundup:
 # 
-##############################################################################
+# 1. changed imports to import from roundup.cgi
+# 2. removed use of ExtensionClass
+# 3. removed use of ComputedAttribute
 """Page Template module
 
 HTML- and XML-based template objects using TAL, TALES, and METAL.
-
-
-Modified for Roundup 0.5 release:
+"""
 
-- changed imports to import from roundup.cgi
-
-"""
-__docformat__ = 'restructuredtext'
-
-__version__='$Revision: 1.4 $'[11:-2]
+__version__='$Revision: 1.5 $'[11:-2]
 
 import sys
 
 from roundup.cgi.TAL.TALParser import TALParser
 from roundup.cgi.TAL.HTMLTALParser import HTMLTALParser
 from roundup.cgi.TAL.TALGenerator import TALGenerator
-from roundup.cgi.TAL.TALInterpreter import TALInterpreter
+# Do not use cStringIO here!  It's not unicode aware. :(
+from roundup.cgi.TAL.TALInterpreter import TALInterpreter, FasterStringIO
 from Expressions import getEngine
-from string import join, strip, rstrip, split, replace, lower, find
-from cStringIO import StringIO
+
 
 class PageTemplate:
     "Page Templates using TAL, TALES, and METAL"
-     
+
     content_type = 'text/html'
     expand = 0
     _v_errors = ()
@@ -48,6 +46,11 @@
     _text = ''
     _error_start = '<!-- Page Template Diagnostics'
 
+    def StringIO(self):
+        # Third-party products wishing to provide a full Unicode-aware
+        # StringIO can do so by monkey-patching this method.
+        return FasterStringIO()
+
     def pt_edit(self, text, content_type):
         if content_type:
             self.content_type = str(content_type)
@@ -71,7 +74,7 @@
                 parent = getattr(self, 'aq_parent', None)
             c['root'] = self
         return c
-    
+
     def pt_render(self, source=0, extra_context={}):
         """Render this Page Template"""
         if not self._v_cooked:
@@ -81,7 +84,7 @@
 
         if self._v_errors:
             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
-        output = StringIO()
+        output = self.StringIO()
         c = self.pt_getContext()
         c.update(extra_context)
 
@@ -107,7 +110,7 @@
             self.pt_render(source=1)
         except:
             return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
-        
+
     def pt_warnings(self):
         if not self._v_cooked:
             self._cook()
@@ -132,7 +135,7 @@
     def write(self, text):
         assert type(text) is type('')
         if text[:len(self._error_start)] == self._error_start:
-            errend = find(text, '-->')
+            errend = text.find('-->')
             if errend >= 0:
                 text = text[errend + 4:]
         if self._text != text:
@@ -140,8 +143,7 @@
         self._cook()
 
     def read(self):
-        if not self._v_cooked:
-            self._cook()
+        self._cook_check()
         if not self._v_errors:
             if not self.expand:
                 return self._text
@@ -151,11 +153,15 @@
                 return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
                         (self._error_start, "%s: %s" % sys.exc_info()[:2],
                          self._text) )
-                                  
+
         return ('%s\n %s\n-->\n%s' % (self._error_start,
-                                      join(self._v_errors, '\n '),
+                                      '\n '.join(self._v_errors),
                                       self._text))
 
+    def _cook_check(self):
+        if not self._v_cooked:
+            self._cook()
+
     def _cook(self):
         """Compile the TAL and METAL statments.
 
@@ -187,7 +193,7 @@
 class _ModuleImporter:
     def __getitem__(self, module):
         mod = __import__(module)
-        path = split(module, '.')
+        path = module.split('.')
         for name in path[1:]:
             mod = getattr(mod, name)
         return mod
--- a/roundup/cgi/PageTemplates/PathIterator.py	Fri May 21 05:36:30 2004 +0000
+++ b/roundup/cgi/PageTemplates/PathIterator.py	Fri May 21 05:56:46 2004 +0000
@@ -1,14 +1,14 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE
-# 
+#
 ##############################################################################
 
 """Path Iterator
@@ -16,13 +16,11 @@
 A TALES Iterator with the ability to use first() and last() on
 subpaths of elements.
 """
-__docformat__ = 'restructuredtext'
 
-__version__='$Revision: 1.2 $'[11:-2]
+__version__='$Revision: 1.3 $'[11:-2]
 
 import TALES
 from Expressions import restrictedTraverse, Undefs, getSecurityManager
-from string import split
 
 class Iterator(TALES.Iterator):
     def __bobo_traverse__(self, REQUEST, name):
@@ -37,7 +35,7 @@
         if name is None:
             return ob1 == ob2
         if isinstance(name, type('')):
-            name = split(name, '/')
+            name = name.split('/')
         name = filter(None, name)
         securityManager = getSecurityManager()
         try:
@@ -46,4 +44,3 @@
         except Undefs:
             return 0
         return ob1 == ob2
-
--- a/roundup/cgi/PageTemplates/PythonExpr.py	Fri May 21 05:36:30 2004 +0000
+++ b/roundup/cgi/PageTemplates/PythonExpr.py	Fri May 21 05:56:46 2004 +0000
@@ -1,29 +1,25 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+# Modified for Roundup:
 # 
-##############################################################################
+# 1. more informative traceback info
 
 """Generic Python Expression Handler
-
-Modified for Roundup 0.5 release:
-
-- more informative traceback info
+"""
 
-"""
-__docformat__ = 'restructuredtext'
-
-__version__='$Revision: 1.5 $'[11:-2]
+__version__='$Revision: 1.6 $'[11:-2]
 
 from TALES import CompilerError
-from string import strip, split, join, replace, lstrip
 from sys import exc_info
 
 class getSecurityManager:
@@ -34,10 +30,10 @@
 
 class PythonExpr:
     def __init__(self, name, expr, engine):
-        self.expr = expr = replace(strip(expr), '\n', ' ')
+        self.expr = expr = expr.strip().replace('\n', ' ')
         try:
             d = {}
-            exec 'def f():\n return %s\n' % strip(expr) in d
+            exec 'def f():\n return %s\n' % expr.strip() in d
             self._f = d['f']
         except:
             raise CompilerError, ('Python expression error:\n'
@@ -50,25 +46,26 @@
             if vname[0] not in '$_':
                 vnames.append(vname)
 
-    def _bind_used_names(self, econtext):
+    def _bind_used_names(self, econtext, _marker=[]):
         # Bind template variables
-        names = {}
+        names = {'CONTEXTS': econtext.contexts}
         vars = econtext.vars
         getType = econtext.getCompiler().getTypes().get
         for vname in self._f_varnames:
-            has, val = vars.has_get(vname)
-            if not has:
+            val = vars.get(vname, _marker)
+            if val is _marker:
                 has = val = getType(vname)
                 if has:
                     val = ExprTypeProxy(vname, val, econtext)
-            if has:
+                    names[vname] = val
+            else:
                 names[vname] = val
         return names
 
     def __call__(self, econtext):
         __traceback_info__ = 'python expression "%s"'%self.expr
         f = self._f
-        f.func_globals.update(self._bind_used_names(econtext))        
+        f.func_globals.update(self._bind_used_names(econtext))
         return f()
 
     def __str__(self):
--- a/roundup/cgi/PageTemplates/TALES.py	Fri May 21 05:36:30 2004 +0000
+++ b/roundup/cgi/PageTemplates/TALES.py	Fri May 21 05:56:46 2004 +0000
@@ -1,30 +1,33 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+# Modified for Roundup:
 # 
-##############################################################################
+# 1. changed imports to import from roundup.cgi
+# 2. implemented ustr as atr
 """TALES
 
 An implementation of a generic TALES engine
-
-Modified for Roundup 0.5 release:
+"""
 
-- changed imports to import from roundup.cgi
-"""
-__docformat__ = 'restructuredtext'
-
-__version__='$Revision: 1.6 $'[11:-2]
+__version__='$Revision: 1.7 $'[11:-2]
 
 import re, sys
 from roundup.cgi import ZTUtils
+from weakref import ref
 from MultiMapping import MultiMapping
+from GlobalTranslationService import getGlobalTranslationService
+
+ustr = str
 
 StringType = type('')
 
@@ -48,8 +51,6 @@
     '''Retain Default'''
 Default = Default()
 
-_marker = []
-
 class SafeMapping(MultiMapping):
     '''Mapping with security declarations and limited method exposure.
 
@@ -64,19 +65,18 @@
     _push = MultiMapping.push
     _pop = MultiMapping.pop
 
-    def has_get(self, key, _marker=[]):
-        v = self.get(key, _marker)
-        return v is not _marker, v
 
 class Iterator(ZTUtils.Iterator):
     def __init__(self, name, seq, context):
         ZTUtils.Iterator.__init__(self, seq)
         self.name = name
-        self._context = context
+        self._context_ref = ref(context)
 
     def next(self):
         if ZTUtils.Iterator.next(self):
-            self._context.setLocal(self.name, self.item)
+            context = self._context_ref()
+            if context is not None:
+                context.setLocal(self.name, self.item)
             return 1
         return 0
 
@@ -138,7 +138,7 @@
             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:
@@ -223,8 +223,7 @@
             expression = self._compiler.compile(expression)
         __traceback_supplement__ = (
             TALESTracebackSupplement, self, expression)
-        v = expression(self)
-        return v
+        return expression(self)
 
     evaluateValue = evaluate
     evaluateBoolean = evaluate
@@ -233,7 +232,10 @@
         text = self.evaluate(expr)
         if text is Default or text is None:
             return text
-        return str(text)
+        if isinstance(text, unicode):
+            return text
+        else:
+            return ustr(text)
 
     def evaluateStructure(self, expr):
         return self.evaluate(expr)
@@ -256,7 +258,15 @@
     def setPosition(self, position):
         self.position = position
 
-
+    def translate(self, domain, msgid, mapping=None,
+                  context=None, target_language=None, default=None):
+        if context is None:
+            context = self.contexts.get('here')
+        return getGlobalTranslationService().translate(
+            domain, msgid, mapping=mapping,
+            context=context,
+            default=default,
+            target_language=target_language)
 
 class TALESTracebackSupplement:
     """Implementation of ITracebackSupplement"""
@@ -272,12 +282,10 @@
         data = self.context.contexts.copy()
         s = pprint.pformat(data)
         if not as_html:
-            return '   - Names:\n      %s' % string.replace(s, '\n', '\n      ')
+            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:
@@ -289,4 +297,3 @@
         return self._name, self._expr
     def __repr__(self):
         return '<SimpleExpr %s %s>' % (self._name, `self._expr`)
-
--- a/roundup/cgi/PageTemplates/__init__.py	Fri May 21 05:36:30 2004 +0000
+++ b/roundup/cgi/PageTemplates/__init__.py	Fri May 21 05:56:46 2004 +0000
@@ -1,22 +1,21 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE
-# 
+#
 ##############################################################################
 __doc__='''Package wrapper for Page Templates
 
 This wrapper allows the Page Template modules to be segregated in a
 separate package.
 
-$Id: __init__.py,v 1.2 2004-02-11 23:55:09 richard Exp $'''
-__docformat__ = 'restructuredtext'
+$Id: __init__.py,v 1.3 2004-05-21 05:56:46 richard Exp $'''
 __version__='$$'[11:-2]
 
 

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