[Zope3-checkins] CVS: Zope3/src/zope/documenttemplate - __init__.py:1.2 documenttemplate.py:1.2 dt_html.py:1.2 dt_if.py:1.2 dt_in.py:1.2 dt_insv.py:1.2 dt_let.py:1.2 dt_raise.py:1.2 dt_return.py:1.2 dt_string.py:1.2 dt_try.py:1.2 dt_util.py:1.2 dt_var.py:1.2 dt_with.py:1.2 pdocumenttemplate.py:1.2

Jim Fulton jim@zope.com
Wed, 25 Dec 2002 09:14:08 -0500


Update of /cvs-repository/Zope3/src/zope/documenttemplate
In directory cvs.zope.org:/tmp/cvs-serv15352/src/zope/documenttemplate

Added Files:
	__init__.py documenttemplate.py dt_html.py dt_if.py dt_in.py 
	dt_insv.py dt_let.py dt_raise.py dt_return.py dt_string.py 
	dt_try.py dt_util.py dt_var.py dt_with.py pdocumenttemplate.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/documenttemplate/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/__init__.py	Wed Dec 25 09:13:36 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.
+#
+##############################################################################
+"""Package wrapper for Document Template
+
+This wrapper allows the (now many) document template modules to be
+segregated in a separate package.
+
+$Id$
+"""
+
+from zope.documenttemplate.documenttemplate import String, HTML
+from zope.documenttemplate.documenttemplate import html_quote


=== Zope3/src/zope/documenttemplate/documenttemplate.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/documenttemplate.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,89 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+'''Document templates with fill-in fields
+
+Document templates provide for creation of textual documents, such as
+HTML pages, from template source by inserting data from python objects
+and name-spaces.
+
+When a document template is created, a collection of default values to
+be inserted may be specified with a mapping object and with keyword
+arguments.
+
+A document templated may be called to create a document with values
+inserted.  When called, an instance, a mapping object, and keyword
+arguments may be specified to provide values to be inserted.  If an
+instance is provided, the document template will try to look up values
+in the instance using getattr, so inheritence of values is supported.
+If an inserted value is a function, method, or class, then an attempt
+will be made to call the object to obtain values.  This allows
+instance methods to be included in documents.
+
+Document templates masquerade as functions, so the python object
+publisher (Bobo) will call templates that are stored as instances of
+published objects. Bobo will pass the object the template was found in
+and the HTTP request object.
+
+Two source formats are supported:
+
+   Extended Python format strings (EPFS) --
+      This format is based on the insertion by name format strings
+      of python with additional format characters, '[' and ']' to
+      indicate block boundaries.  In addition, parameters may be
+      used within formats to control how insertion is done.
+
+      For example:
+
+         %%(date fmt=DayOfWeek upper)s
+
+      causes the contents of variable 'date' to be inserted using
+      custom format 'DayOfWeek' and with all lower case letters
+      converted to upper case.
+
+   HTML --
+      This format uses HTML server-side-include syntax with
+      commands for inserting text. Parameters may be included to
+      customize the operation of a command.
+
+      For example:
+
+         <dtml-var total fmt=12.2f>
+
+      is used to insert the variable 'total' with the C format
+      '12.2f'.
+
+Document templates support conditional and sequence insertion
+
+    Document templates extend python string substitition rules with a
+    mechanism that allows conditional insertion of template text and that
+    allows sequences to be inserted with element-wise insertion of
+    template text.
+
+Document Templates may be created 2 ways by default:
+
+    documenttemplate.String -- Creates a document templated from a
+        string using an extended form of python string formatting.
+
+    documenttemplate.HTML -- Creates a document templated from a
+        string using HTML server-side-include rather than
+        python-format-string syntax.
+
+$Id$
+'''
+
+ParseError='Document Template Parse Error'
+
+from zope.documenttemplate.dt_string import String
+from zope.documenttemplate.dt_html import HTML
+from zope.documenttemplate.dt_util import html_quote


=== Zope3/src/zope/documenttemplate/dt_html.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_html.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,204 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""HTML formated DocumentTemplates
+
+$Id$
+"""
+import re
+from zope.documenttemplate.dt_string import String
+from zope.documenttemplate.dt_util import ParseError, str
+
+class DTMLRegExClass:
+
+    def search(self, text, start=0,
+               name_match=re.compile('[\0- ]*[a-zA-Z]+[\0- ]*').match,
+               start_search=re.compile('[<&]').search,
+               ent_name=re.compile('[-a-zA-Z0-9_.]+').match
+               ):
+
+        while 1:
+            mo = start_search(text, start)
+            if mo is None:
+                return None
+            s = mo.start(0)
+
+            if text[s:s+6] == '<dtml-':
+                e = n = s+6
+                while 1:
+                    e = text.find('>', e+1)
+                    if e < 0:
+                        return None
+                    if len(text[n:e].split('"'))%2:
+                        # check for even number of "s inside
+                        break
+
+                en = 1
+                end = ''
+
+            elif text[s:s+7] == '</dtml-':
+                e=n=s+7
+                while 1:
+                    e=text.find('>',e+1)
+                    if e < 0:
+                        return None
+                    if len(text[n:e].split('"'))%2:
+                        # check for even number of "s inside
+                        break
+
+                en=1
+                end='/'
+
+            else:
+                if text[s:s+5] == '&dtml' and text[s+5] in '.-':
+                    n=s+6
+                    e=text.find(';', n)
+                    if e >= 0:
+                        args=text[n:e]
+                        l=len(args)
+                        mo = ent_name(args)
+                        if mo is not None and mo.end(0)-mo.start(0) == l:
+                            d=self.__dict__
+                            if text[s+5] == '-':
+                                d[1] = d['end'] = ''
+                                d[2] = d['name'] = 'var'
+                                d[0] = text[s:e+1]
+                                d[3] = d['args'] = args + ' html_quote'
+                                self._start = s
+                                return self
+                            else:
+                                nn=args.find('-')
+                                if nn >= 0 and nn < l-1:
+                                    d[1]=d['end']=''
+                                    d[2]=d['name']='var'
+                                    d[0]=text[s:e+1]
+                                    args=(args[nn+1:]+' '+
+                                          args[:nn].replace('.', ' '))
+                                    d[3]=d['args']=args
+                                    self._start = s
+                                    return self
+
+                start = s + 1
+                continue
+
+            break
+
+        mo=name_match(text,n)
+        if mo is None:
+            return None
+        l = mo.end(0) - mo.start(0)
+        a=n+l
+        name=text[n:a].strip()
+
+        args=text[a:e].strip()
+
+        d=self.__dict__
+        d[0]=text[s:e+en]
+        d[1]=d['end']=end
+        d[2]=d['name']=name
+        d[3]=d['args']=args
+
+        self._start = s
+        return self
+
+
+    def group(self, *args):
+        get=self.__dict__.get
+        if len(args)==1:
+            return get(args[0])
+        return tuple(map(get, args))
+
+
+    def start(self, *args):
+        return self._start
+
+
+
+class HTML(String):
+    """HTML Document Templates
+
+    HTML Document templates use HTML server-side-include syntax,
+    rather than Python format-string syntax.  Here's a simple example:
+
+      <dtml-in results>
+        <dtml-var name>
+      </dtml-in>
+
+    HTML document templates quote HTML tags in source when the
+    template is converted to a string.  This is handy when templates
+    are inserted into HTML editing forms.
+    """
+
+    def tagre(self):
+        return DTMLRegExClass()
+
+
+    def parseTag(self, tagre, command=None, sargs=''):
+        """Parse a tag using an already matched re
+
+        Return: tag, args, command, coname
+
+        where: tag is the tag,
+               args is the tag\'s argument string,
+               command is a corresponding command info structure if the
+                  tag is a start tag, or None otherwise, and
+               coname is the name of a continue tag (e.g. else)
+                 or None otherwise
+        """
+        tag, end, name, args, =tagre.group(0, 'end', 'name', 'args')
+        args=args.strip()
+        if end:
+            if not command or name != command.name:
+                raise ParseError, ('unexpected end tag', tag)
+            return tag, args, None, None
+
+        if command and name in command.blockContinuations:
+
+            if name=='else' and args:
+                # Waaaaaah! Have to special case else because of
+                # old else start tag usage. Waaaaaaah!
+                l=len(args)
+                if not (args==sargs or
+                        args==sargs[:l] and sargs[l:l+1] in ' \t\n'):
+                    return tag, args, self.commands[name], None
+
+            return tag, args, None, name
+
+        try: return tag, args, self.commands[name], None
+        except KeyError:
+            raise ParseError, ('Unexpected tag', tag)
+
+
+    def SubTemplate(self, name):
+        return HTML('', __name__=name)
+
+    def varExtra(self,tagre):
+        return 's'
+
+    def quotedHTML(self,
+                   text=None,
+                   character_entities=(
+                       (('&'), '&amp;'),
+                       (("<"), '&lt;' ),
+                       ((">"), '&gt;' ),
+                       (('"'), '&quot;'))): #"
+        if text is None: text=self.read_raw()
+        for re,name in character_entities:
+            if text.find(re) >= 0: text=name.join(text.split(re))
+        return text
+
+    errQuote__roles__=()
+    errQuote=quotedHTML
+
+    def __str__(self):
+        return self.quotedHTML()


=== Zope3/src/zope/documenttemplate/dt_if.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_if.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,146 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Conditional insertion
+
+       Conditional insertion is performed using 'if' and 'else'
+       commands.
+
+       To include text when an object is true using the EPFS
+       format, use::
+
+          %(if name)[
+               text
+          %(if name)]
+
+       To include text when an object is true using the HTML
+       format, use::
+
+          <dtml-if name>
+               text
+          </dtml-if name>
+
+       where 'name' is the name bound to the object.
+
+       To include text when an object is false using the EPFS
+       format, use::
+
+          %(else name)[
+               text
+          %(else name)]
+
+       To include text when an object is false using the HTML
+       format, use::
+
+          <dtml-else name>
+               text
+          </dtml-else name>
+
+       Finally to include text when an object is true and to
+       include different text when the object is false using the
+       EPFS format, use::
+
+          %(if name)[
+               true text
+          %(if name)]
+          %(else name)[
+               false text
+          %(else name)]
+
+       and to include text when an object is true and to
+       include different text when the object is false using the
+       HTML format, use::
+
+          <dtml-if name>
+               true text
+          <dtml-else name>
+               false text
+          </dtml-if name>
+
+       Notes:
+
+       - if a variable is nor defined, it is considered to be false.
+
+       - A variable if only evaluated once in an 'if' tag.  If the value
+         is used inside the tag, including in enclosed tags, the
+         variable is not reevaluated.
+
+$Id$
+"""
+from zope.documenttemplate.dt_util import ParseError, parse_params, name_param, str
+
+class If:
+    blockContinuations = 'else', 'elif'
+    name = 'if'
+    elses = None
+    expr = ''
+
+    def __init__(self, blocks):
+        tname, args, section = blocks[0]
+        args = parse_params(args, name='', expr='')
+        name,expr = name_param(args,'if',1)
+        self.__name__ = name
+        if expr is None:
+            cond = name
+        else:
+            cond = expr.eval
+        sections = [cond, section.blocks]
+
+        if blocks[-1][0] == 'else':
+            tname, args, section = blocks[-1]
+            del blocks[-1]
+            args = parse_params(args, name='')
+            if args:
+                ename,expr=name_param(args,'else',1)
+                if ename != name:
+                    raise ParseError, ('name in else does not match if', 'in')
+            elses=section.blocks
+        else: elses = None
+
+        for tname, args, section in blocks[1:]:
+            if tname == 'else':
+                raise ParseError, (
+                    'more than one else tag for a single if tag', 'in')
+            args = parse_params(args, name='', expr='')
+            name,expr = name_param(args, 'elif', 1)
+            if expr is None:
+                cond = name
+            else:
+                cond = expr.eval
+            sections.append(cond)
+            sections.append(section.blocks)
+
+        if elses is not None:
+            sections.append(elses)
+
+        self.simple_form = tuple(sections)
+
+
+class Unless:
+    name = 'unless'
+    blockContinuations = ()
+
+    def __init__(self, blocks):
+        tname, args, section = blocks[0]
+        args=parse_params(args, name='', expr='')
+        name,expr=name_param(args, 'unless', 1)
+        if expr is None:
+            cond = name
+        else:
+            cond = expr.eval
+        self.simple_form = (cond, None, section.blocks)
+
+
+class Else(Unless):
+    # The else tag is included for backward compatibility and is deprecated.
+    name = 'else'


=== Zope3/src/zope/documenttemplate/dt_in.py 1.1 => 1.2 === (688/788 lines abridged)
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_in.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,785 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Sequence insertion
+
+       A sequence may be inserted using an 'in' command.  The 'in'
+       command specifies the name of a sequence object and text to
+       be inserted for each element in the sequence.
+
+       The EPFS syntax for the in command is::
+
+          %(in name)[
+               text
+          %(in name)]
+
+       The HTML syntax for the in command is::
+
+          <dtml-in name>
+               text
+          </dtml-in name>
+
+      See the example below that shows how 'if', 'else', and 'in' commands
+      may be combined to display a possibly empty list of objects.
+
+      The text included within an 'in' command will be refered to
+      as an 'in' block.
+
+    Synopsis
+
+      If the variable 'sequence' exists as a sequence, a simple case
+      of the 'in' tag is used as follows::
+
+         <dtml-in sequence>some markup</dtml-in>
+
+      A more complete case is used as follows::
+
+        <dtml-in sequence sort=age>

[-=- -=- -=- 688 lines omitted -=- -=- -=-]

+                                akey = akey()
+                            except:
+                                pass
+                        k.append(akey)
+                else: # One sort key.
+                    try:
+                        if mapping:
+                            k = v[sort]
+                        else:
+                            k = getattr(v, sort)
+                    except AttributeError, KeyError:
+                        k = None
+                    if not basic_type(type(k)):
+                        try:
+                            k = k()
+                        except:
+                            pass
+
+            s.append((k,client))
+
+        s.sort()
+
+        sequence = []
+        for k, client in s:
+            sequence.append(client)
+        return sequence
+
+
+    def reverse_sequence(self, sequence):
+        s = list(sequence)
+        s.reverse()
+        return s
+
+
+basic_type = {StringType: 1, IntType: 1, FloatType: 1, TupleType: 1,
+              ListType: 1, NoneType: 1}.has_key
+
+def int_param(params, md, name, default=0, st=StringType):
+    try:
+        v = params[name]
+    except:
+        v = default
+    if v:
+        try:
+            v = v.atoi()
+        except:
+            v = md[v]
+            if isinstance(v, st):
+                v = v.atoi()
+    return v


=== Zope3/src/zope/documenttemplate/dt_insv.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_insv.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,431 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Sequence variables support
+
+$Id$
+"""
+
+from math import sqrt
+from types import IntType, TupleType
+
+try:
+    import Missing
+    mv = Missing.Value
+except: mv = None
+
+
+class sequence_variables:
+
+    def __init__(self, items=None, query_string='', start_name_re=None):
+        self.items = items
+        self.query_string = query_string
+        self.start_name_re = start_name_re
+
+        self.data = data = {
+            'previous-sequence': 0,
+            'next-sequence': 0,
+            'sequence-start': 1,
+            'sequence-end': 0,
+            }
+
+
+    def __len__(self):
+        return 1
+
+    def number(self, index):
+        return index+1
+
+    def even(self, index):
+        return index%2 == 0
+
+    def odd(self, index):
+        return index%2
+
+    def letter(self, index):
+        return chr(ord('a') + index)
+
+    def Letter(self, index):
+        return chr(ord('A') + index)
+
+    def key(self,index):
+        return self.items[index][0]
+
+    def item(self,index, tt = TupleType):
+        i = self.items[index]
+        if isinstance(i, tt) and len(i)==2:
+            return i[1]
+        return i
+
+    def roman(self, index):
+        return self.Roman(index).lower()
+
+    def Roman(self,num):
+        # Force number to be an integer value
+        num = int(num) + 1
+
+        # Initialize roman as an empty string
+        roman = ''
+
+        while num >= 1000:
+            num = num - 1000
+            roman = '%sM' % roman
+
+        while num >= 500:
+            num = num - 500
+            roman = '%sD' % roman
+
+        while num >= 100:
+            num = num - 100
+            roman = '%sC' % roman
+
+        while num >= 50:
+            num = num - 50
+            roman = '%sL' % roman
+
+        while num >= 10:
+            num = num - 10
+            roman = '%sX' % roman
+
+        while num >= 5:
+            num = num - 5
+            roman = '%sV' % roman
+
+        while num < 5 and num >= 1:
+            num = num - 1
+            roman = '%sI' % roman
+
+        # Replaces special cases in Roman Numerals
+
+        roman = sub('DCCCC', 'CM', roman)
+        roman = sub('CCCC', 'CD', roman)
+        roman = sub('LXXXX', 'XC', roman)
+        roman = sub('XXXX', 'XL', roman)
+        roman = sub('VIIII', 'IX', roman)
+        roman = sub('IIII', 'IV', roman)
+
+        return roman
+
+
+    def value(self, index, name):
+        data = self.data
+        item = self.items[index]
+        if isinstance(item, TupleType) and len(item)==2:
+            item = item[1]
+        if data['mapping']:
+            return item[name]
+        return getattr(item, name)
+
+    def first(self, name, key=''):
+        data = self.data
+        if data['sequence-start']:
+            return 1
+        index = data['sequence-index']
+        return self.value(index, name) != self.value(index-1, name)
+
+    def last(self, name, key=''):
+        data = self.data
+        if data['sequence-end']:
+            return 1
+        index = data['sequence-index']
+        return self.value(index, name) != self.value(index+1, name)
+
+    def length(self, ignored):
+        l=self.data['sequence-length'] = len(self.items)
+        return l
+
+    def query(self, *ignored):
+        if self.start_name_re is None:
+            raise KeyError, 'sequence-query'
+        query_string = self.query_string
+        while query_string and query_string[:1] in '?&':
+            query_string = query_string[1:]
+        while query_string[-1:] == '&':
+            query_string = query_string[:-1]
+        if query_string:
+            query_string = '&%s&' % query_string
+            re = self.start_name_re
+            l = re.search_group(query_string, (0,))
+            if l:
+                v = l[1]
+                l = l[0]
+                query_string = (query_string[:l] +
+                                query_string[l + len(v) - 1:])
+            query_string = '?' + query_string[1:]
+        else:
+            query_string = '?'
+        self.data['sequence-query'] = query_string
+        return query_string
+
+
+    statistic_names = (
+        'total', 'count', 'min', 'max', 'median', 'mean',
+        'variance', 'variance-n','standard-deviation', 'standard-deviation-n',
+        )
+
+    def statistics(self, name, key):
+        items = self.items
+        data = self.data
+        mapping = data['mapping']
+        count = sum = sumsq = 0
+        min = max = None
+        scount = smin = smax = None
+        values = []
+        svalues = []
+        for item in items:
+            try:
+                if mapping:
+                    item = item[name]
+                else:
+                    item = getattr(item, name)
+                try:
+                    if item is mv:
+                        item = None
+                    if isinstance(item, IntType):
+                        s = item * long(item)
+                    else:
+                        s = item * item
+                    sum = sum + item
+                    sumsq = sumsq + s
+                    values.append(item)
+                    if min is None:
+                        min = max = item
+                    else:
+                        if item < min:
+                            min = item
+                        if item > max:
+                            max = item
+                except:
+                    if item is not None and item is not mv:
+                        if smin is None:
+                            smin = smax = item
+                        else:
+                            if item < smin:
+                                smin = item
+                            if item > smax:
+                                smax = item
+                        svalues.append(item)
+            except: pass
+
+        # Initialize all stats to empty strings:
+        for stat in self.statistic_names:
+            data['%s-%s' % (stat,name)] = ''
+
+        count = len(values)
+        try: # Numeric statistics
+            n = float(count)
+            mean = sum/n
+            sumsq = sumsq/n - mean*mean
+            data['mean-%s' % name] = mean
+            data['total-%s' % name] = sum
+            data['variance-n-%s' % name] = sumsq
+            data['standard-deviation-n-%s' % name] = sqrt(sumsq)
+            if count > 1:
+                sumsq = sumsq * n/(n-1)
+                data['variance-%s' % name] = sumsq
+                data['standard-deviation-%s' % name] = sqrt(sumsq)
+            else:
+                data['variance-%s' % name] = ''
+                data['standard-deviation-%s' % name] = ''
+        except:
+            if min is None: min, max, values = smin, smax, svalues
+            else:
+                if smin < min:
+                    min = smin
+                if smax > max:
+                    max = smax
+                values = values + svalues
+            count = len(values)
+
+        data['count-%s' % name] = count
+        # data['_values']=values
+        if min is not None:
+            data['min-%s' % name] = min
+            data['max-%s' % name] = max
+            values.sort()
+            if count == 1:
+                data['median-%s' % name] = min
+            else:
+                n=count+1
+                if n/2 * 2 == n:
+                    data['median-%s' % name] = values[n/2 - 1]
+                else:
+                    n = n/2
+                    try:
+                        data['median-%s' % name] = (values[n]+values[n-1])/2
+                    except:
+                        try:
+                            data['median-%s' % name] = (
+                                "between %s and %s" % (values[n], values[n-1]))
+                        except: pass
+
+        return data[key]
+
+    def next_batches(self, suffix='batches', key=''):
+        if suffix != 'batches':
+            raise KeyError, key
+        data = self.data
+        sequence = self.items
+        try:
+            if not data['next-sequence']:
+                return ()
+            sz = data['sequence-step-size']
+            start = data['sequence-step-start']
+            end = data['sequence-step-end']
+            l = len(sequence)
+            orphan = data['sequence-step-orphan']
+            overlap = data['sequence-step-overlap']
+        except:
+            AttributeError, 'next-batches'
+        r = []
+        while end < l:
+            start, end, spam = opt(end+1-overlap, 0, sz, orphan, sequence)
+            v = sequence_variables(self.items,
+                                   self.query_string, self.start_name_re)
+            d = v.data
+            d['batch-start-index'] = start-1
+            d['batch-end-index'] = end-1
+            d['batch-size'] = end+1-start
+            d['mapping'] = data['mapping']
+            r.append(v)
+        data['next-batches'] = r
+        return r
+
+    def previous_batches(self, suffix='batches', key=''):
+        if suffix != 'batches':
+            raise KeyError, key
+        data = self.data
+        sequence = self.items
+        try:
+            if not data['previous-sequence']:
+                return ()
+            sz = data['sequence-step-size']
+            start = data['sequence-step-start']
+            end = data['sequence-step-end']
+            l = len(sequence)
+            orphan = data['sequence-step-orphan']
+            overlap = data['sequence-step-overlap']
+        except:
+            AttributeError, 'previous-batches'
+        r = []
+        while start > 1:
+            start, end, spam = opt(0, start-1+overlap, sz, orphan, sequence)
+            v = sequence_variables(self.items,
+                                   self.query_string, self.start_name_re)
+            d = v.data
+            d['batch-start-index'] = start-1
+            d['batch-end-index'] = end-1
+            d['batch-size'] = end+1-start
+            d['mapping'] = data['mapping']
+            r.append(v)
+        r.reverse()
+        data['previous-batches'] = r
+        return r
+
+
+    special_prefixes = {
+        'first': first,
+        'last': last,
+        'previous': previous_batches,
+        'next': next_batches,
+        # These two are for backward compatability with a missfeature:
+        'sequence-index': lambda self, suffix, key: self['sequence-'+suffix],
+        'sequence-index-is': lambda self, suffix, key: self['sequence-'+suffix],
+        }
+    for n in statistic_names:
+        special_prefixes[n] = statistics
+
+    def __getitem__(self,key,
+                    special_prefixes=special_prefixes,
+                    special_prefix=special_prefixes.has_key
+                    ):
+        data = self.data
+        if data.has_key(key):
+            return data[key]
+
+        l = key.rfind('-')
+        if l < 0:
+            raise KeyError, key
+
+        suffix = key[l+1:]
+        prefix = key[:l]
+
+        if hasattr(self, suffix):
+            try:
+                v = data[prefix+'-index']
+            except:
+                pass
+            else:
+                return getattr(self, suffix)(v)
+
+        if special_prefix(prefix):
+            return special_prefixes[prefix](self, suffix, key)
+
+        if prefix[-4:] == '-var':
+            prefix = prefix[:-4]
+            try:
+                return self.value(data[prefix+'-index'], suffix)
+            except:
+                pass
+
+        if key == 'sequence-query':
+            return self.query()
+
+        raise KeyError, key
+
+
+
+def sub(s1, s2, src):
+    return s2.join(src.split(s1))
+
+def opt(start, end, size, orphan, sequence):
+    if size < 1:
+        if start > 0 and end > 0 and end >= start:
+            size = end+1 - start
+        else: size = 7
+
+    if start > 0:
+
+        try:
+            sequence[start-1]
+        except:
+            start = len(sequence)
+
+        if end > 0:
+            if end < start:
+                end = start
+        else:
+            end = start + size-1
+            try:
+                sequence[end+orphan-1]
+            except:
+                end = len(sequence)
+
+    elif end > 0:
+        try:
+            sequence[end-1]
+        except:
+            end=len(sequence)
+        start = end+1 - size
+        if start - 1 < orphan:
+            start = 1
+
+    else:
+        start = 1
+        end = start + size-1
+        try:
+            sequence[end+orphan-1]
+        except:
+            end = len(sequence)
+
+    return start, end, size


=== Zope3/src/zope/documenttemplate/dt_let.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_let.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,124 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""The Let tag was contributed to Zope by and is copyright, 1999
+   Phillip J. Eby.  Permission has been granted to release the Let tag
+   under the Zope Public License.
+
+
+   Let name=value...
+
+   The 'let' tag is used to bind variables to values within a block.
+
+   The text enclosed in the let tag is rendered using information
+   from the given variables or expressions.
+
+   For example::
+
+     <dtml-let foofunc="foo()" my_bar=bar>
+       foo() = <dtml-var foofunc>,
+       bar = <dtml-var my_bar>
+     </dtml-let>
+
+   Notice that both 'name' and 'expr' style attributes may be used to
+   specify data.  'name' style attributes (e.g. my_bar=bar) will be
+   rendered as they are for var/with/in/etc.  Quoted attributes will
+   be treated as Python expressions.
+
+   Variables are processed in sequence, so later assignments can
+   reference and/or overwrite the results of previous assignments,
+   as desired.
+
+$Id$
+"""
+from zope.documenttemplate.dt_util import render_blocks, Eval, ParseError
+from zope.documenttemplate.dt_util import str # Probably needed due to hysterical pickles.
+
+from types import StringType
+import re
+
+class Let:
+    blockContinuations = ()
+    name = 'let'
+
+    def __init__(self, blocks):
+        tname, args, section = blocks[0]
+        self.__name__ = args
+        self.section = section.blocks
+        self.args = args = parse_let_params(args)
+
+        for i in range(len(args)):
+            name,expr = args[i]
+            if expr[:1] == '"' and expr[-1:] == '"' and len(expr) > 1:
+                # expr shorthand
+                expr = expr[1:-1]
+                try:
+                    args[i] = name, Eval(expr).eval
+                except SyntaxError, v:
+                    m,(huh,l,c,src) = v
+                    raise ParseError, (
+                        '<strong>Expression (Python) Syntax error</strong>:'
+                        '\n<pre>\n%s\n</pre>\n' % v[0],
+                        'let')
+
+
+    def render(self, md):
+        d = {}
+        md._push(d)
+        try:
+            for name,expr in self.args:
+                if isinstance(expr, StringType):
+                    d[name] = md[expr]
+                else:
+                    d[name] = expr(md)
+            return render_blocks(self.section, md)
+        finally:
+            md._pop(1)
+
+
+    __call__ = render
+
+
+
+def parse_let_params(text,
+            result=None,
+            tag='let',
+            parmre=re.compile('([\000- ]*([^\000- ="]+)=([^\000- ="]+))'),
+            qparmre=re.compile('([\000- ]*([^\000- ="]+)="([^"]*)")'),
+            **parms):
+
+    result = result or []
+
+    mo = parmre.match(text)
+    mo1 = qparmre.match(text)
+
+    if mo is not None:
+        name = mo.group(2)
+        value = mo.group(3)
+        l = len(mo.group(1))
+    elif mo1 is not None:
+        name = mo1.group(2)
+        value = '"%s"' % mo1.group(3)
+        l = len(mo1.group(1))
+    else:
+        if not text or not text.strip():
+            return result
+        raise ParseError, ('invalid parameter: "%s"' % text, tag)
+
+    result.append((name,value))
+
+    text = text[l:].strip()
+    if text:
+        return apply(parse_let_params, (text, result,tag), parms)
+    else:
+        return result


=== Zope3/src/zope/documenttemplate/dt_raise.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_raise.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,60 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+'''Raising exceptions
+
+   Errors can be raised from DTML using the 'raise' tag.
+
+   For example::
+
+    <dtml-if expr="condition_that_tests_input">
+       <dtml-raise type="Input Error">
+           The value you entered is not valid
+       </dtml-raise>
+    </dtml-if>
+
+$Id$
+'''
+from zope.documenttemplate.dt_util import parse_params, name_param, render_blocks, str
+
+class Raise:
+    blockContinuations = ()
+    name = 'raise'
+    expr = ''
+
+    def __init__(self, blocks):
+        tname, args, section = blocks[0]
+        self.section=section.blocks
+        args=parse_params(args, type='', expr='')
+        self.__name__, self.expr = name_param(args, 'raise', 1, attr='type')
+
+    def render(self, md):
+        expr = self.expr
+        if expr is None:
+            t = self.__name__
+            if t[-5:] == 'Error' and __builtins__.has_key(t):
+                t = __builtins__[t]
+        else:
+            try:
+                t = expr.eval(md)
+            except:
+                t = 'Invalid Error Type Expression'
+
+        try:
+            v = render_blocks(self.section,md)
+        except:
+            v = 'Invalid Error Value'
+
+        raise t, v
+
+    __call__ = render


=== Zope3/src/zope/documenttemplate/dt_return.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_return.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope.documenttemplate.dt_util import parse_params, name_param, html_quote, str
+
+
+class ReturnTag:
+    name = 'return'
+    expr = None
+
+    def __init__(self, args):
+        args = parse_params(args, name='', expr='')
+        name, expr = name_param(args,'var',1)
+        self.__name__, self.expr = name, expr
+
+    def render(self, md):
+        name = self.__name__
+        val = self.expr
+        if val is None:
+            val = md[name]
+        else:
+            val = val.eval(md)
+
+        raise DTReturn(val)
+
+    __call__ = render
+
+
+class DTReturn:
+    def __init__(self, v):
+        self.v = v


=== Zope3/src/zope/documenttemplate/dt_string.py 1.1 => 1.2 === (407/507 lines abridged)
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_string.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,504 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import re, thread
+
+from zope.documenttemplate.dt_util import ParseError, InstanceDict, TemplateDict, render_blocks, str
+from zope.documenttemplate.dt_var import Var, Call, Comment
+from zope.documenttemplate.dt_return import ReturnTag, DTReturn
+
+from types import TupleType
+
+_marker = []  # Create a new marker object.
+
+class String:
+    """Document templates defined from strings.
+
+    Document template strings use an extended form of python string
+    formatting.  To insert a named value, simply include text of the
+    form: '%(name)x', where 'name' is the name of the value and 'x' is
+    a format specification, such as '12.2d'.
+
+    To intrduce a block such as an 'if' or an 'in' or a block continuation,
+    such as an 'else', use '[' as the format specification.  To
+    terminate a block, ise ']' as the format specification, as in::
+
+      %(in results)[
+        %(name)s
+      %(in results)]
+
+    """
+
+    # Document Templates masquerade as functions:
+    class func_code:

[-=- -=- -=- 407 lines omitted -=- -=- -=-]

+        level = md.level
+        if level > 200:
+            raise SystemError, ('infinite recursion in document template')
+        md.level = level+1
+
+        if client is not None:
+            if isinstance(client, TupleType):
+                # if client is a tuple, it represents a "path" of clients
+                # which should be pushed onto the md in order.
+                for ob in client:
+                    push(InstanceDict(ob, md)) # Circ. Ref. 8-|
+                    pushed += 1
+            else:
+                # otherwise its just a normal client object.
+                push(InstanceDict(client, md)) # Circ. Ref. 8-|
+                pushed += 1
+
+        if self._vars:
+            push(self._vars)
+            pushed += 1
+
+        if kw:
+            push(kw)
+            pushed += 1
+
+        try:
+            try:
+                result = render_blocks(self._v_blocks, md)
+            except DTReturn, v:
+                result = v.v
+            return result
+        finally:
+            if pushed:
+                md._pop(pushed) # Get rid of circular reference!
+            md.level=level # Restore previous level
+
+
+    validate=None
+
+    def __str__(self):
+        return self.read()
+
+
+    def __getstate__(self, _special=('_v_', '_p_')):
+        # Waaa, we need _v_ behavior but we may not subclass Persistent
+        d={}
+        for k, v in self.__dict__.items():
+            if k[:3] in _special: continue
+            d[k] = v
+        return d


=== Zope3/src/zope/documenttemplate/dt_try.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_try.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,226 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import sys, traceback
+from StringIO import StringIO
+from zope.documenttemplate.dt_util import ParseError, parse_params, render_blocks
+from zope.documenttemplate.dt_util import namespace, InstanceDict
+from zope.documenttemplate.dt_return import DTReturn
+
+from types import StringType
+
+
+class Try:
+    """Zope DTML Exception handling
+
+    usage:
+
+    <dtml-try>
+    <dtml-except SomeError AnotherError>
+    <dtml-except YetAnotherError>
+    <dtml-except>
+    <dtml-else>
+    </dtml-try>
+
+    or:
+
+    <dtml-try>
+    <dtml-finally>
+    </dtml-try>
+
+    The DTML try tag functions quite like Python's try command.
+
+    The contents of the try tag are rendered. If an exception is raised,
+    then control switches to the except blocks. The first except block to
+    match the type of the error raised is rendered. If an except block has
+    no name then it matches all raised errors.
+
+    The try tag understands class-based exceptions, as well as string-based
+    exceptions. Note: the 'raise' tag raises string-based exceptions.
+
+    Inside the except blocks information about the error is available via
+    three variables.
+
+      'error_type' -- This variable is the name of the exception caught.
+
+      'error_value' -- This is the caught exception's value.
+
+      'error_tb' -- This is a traceback for the caught exception.
+
+    The optional else block is rendered when no exception occurs in the
+    try block. Exceptions in the else block are not handled by the preceding
+    except blocks.
+
+    The try..finally form specifies a `cleanup` block, to be rendered even
+    when an exception occurs. Note that any rendered result is discarded if
+    an exception occurs in either the try or finally blocks. The finally block
+    is only of any use if you need to clean up something that will not be
+    cleaned up by the transaction abort code.
+
+    The finally block will always be called, wether there was an exception in
+    the try block or not, or wether or not you used a return tag in the try
+    block. Note that any output of the finally block is discarded if you use a
+    return tag in the try block.
+
+    If an exception occurs in the try block, and an exception occurs in the
+    finally block, or you use the return tag in that block, any information
+    about that first exception is lost. No information about the first
+    exception is available in the finally block. Also, if you use a return tag
+    in the try block, and an exception occurs in the finally block or you use
+    a return tag there as well, the result returned in the try block will be
+    lost.
+
+    Original version by Jordan B. Baker.
+
+    Try..finally and try..else implementation by Martijn Pieters.
+    """
+
+    name = 'try'
+    blockContinuations = 'except', 'else', 'finally'
+    finallyBlock = None
+    elseBlock = None
+
+    def __init__(self, blocks):
+        tname, args, section = blocks[0]
+
+        self.args = parse_params(args)
+        self.section = section.blocks
+
+
+        # Find out if this is a try..finally type
+        if len(blocks) == 2 and blocks[1][0] == 'finally':
+            self.finallyBlock = blocks[1][2].blocks
+
+        # This is a try [except]* [else] block.
+        else:
+            # store handlers as tuples (name,block)
+            self.handlers = []
+            defaultHandlerFound = 0
+
+            for tname, nargs, nsection in blocks[1:]:
+                if tname == 'else':
+                    if not self.elseBlock is None:
+                        raise ParseError, (
+                            'No more than one else block is allowed',
+                            self.name)
+                    self.elseBlock = nsection.blocks
+
+                elif tname == 'finally':
+                    raise ParseError, (
+                        'A try..finally combination cannot contain '
+                        'any other else, except or finally blocks',
+                        self.name)
+
+                else:
+                    if not self.elseBlock is None:
+                        raise ParseError, (
+                            'The else block should be the last block '
+                            'in a try tag', self.name)
+
+                    for errname in nargs.split():
+                        self.handlers.append((errname, nsection.blocks))
+                    if nargs.strip() == '':
+                        if defaultHandlerFound:
+                            raise ParseError, (
+                                'Only one default exception handler '
+                                'is allowed', self.name)
+                        else:
+                            defaultHandlerFound = 1
+                            self.handlers.append(('', nsection.blocks))
+
+
+    def render(self, md):
+        if (self.finallyBlock is None):
+            return self.render_try_except(md)
+        else:
+            return self.render_try_finally(md)
+
+
+    def render_try_except(self, md):
+        result = ''
+
+        # first we try to render the first block
+        try:
+            result = render_blocks(self.section, md)
+        except DTReturn:
+            raise
+        except:
+            # but an error occurs.. save the info.
+            t, v = sys.exc_info()[:2]
+            if isinstance(t, StringType):
+                errname = t
+            else:
+                errname = t.__name__
+
+            handler = self.find_handler(t)
+
+            if handler is None:
+                # we didn't find a handler, so reraise the error
+                raise
+
+            # found the handler block, now render it
+            try:
+                f = StringIO()
+                traceback.print_exc(100,f)
+                error_tb = f.getvalue()
+                ns = namespace(md, error_type=errname, error_value=v,
+                    error_tb=error_tb)[0]
+                md._push(InstanceDict(ns,md))
+                return render_blocks(handler, md)
+            finally:
+                md._pop(1)
+
+        else:
+            # No errors have occured, render the optional else block
+            if (self.elseBlock is None):
+                return result
+            else:
+                return result + render_blocks(self.elseBlock, md)
+
+
+    def render_try_finally(self, md):
+        result = ''
+        # first try to render the first block
+        try:
+            result = render_blocks(self.section, md)
+        # Then handle finally block
+        finally:
+            result = result + render_blocks(self.finallyBlock, md)
+        return result
+
+
+    def find_handler(self,exception):
+        "recursively search for a handler for a given exception"
+        if isinstance(exception, StringType):
+            for e,h in self.handlers:
+                if exception==e or e=='':
+                    return h
+            else:
+                return None
+        for e,h in self.handlers:
+            if e==exception.__name__ or e=='' or self.match_base(exception,e):
+                return h
+        return None
+
+    def match_base(self,exception,name):
+        for base in exception.__bases__:
+            if base.__name__ == name or self.match_base(base, name):
+                return 1
+        return None
+
+    __call__ = render


=== Zope3/src/zope/documenttemplate/dt_util.py 1.1 => 1.2 === (411/511 lines abridged)
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_util.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,508 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import re, math
+import whrandom
+
+from __builtin__ import str  # XXX needed for pickling (legacy)
+from types import ListType, StringType, TupleType
+
+from zope.documenttemplate.pdocumenttemplate import InstanceDict, TemplateDict, render_blocks
+
+
+ParseError='Document Template Parse Error'
+ValidationError='Unauthorized'
+
+
+def html_quote(v, name='(Unknown name)', md={},
+               character_entities=(
+                       (('&'),    '&amp;'),
+                       (('<'),    '&lt;' ),
+                       (('>'),    '&gt;' ),
+                       (('"'),    '&quot;'))): #"
+    text = str(v)
+    for re, name in character_entities:
+        text = text.replace(re, name)
+    return text
+
+
+def int_param(params, md, name, default=0):
+    try:
+        v = params[name]
+    except:
+        v = default

[-=- -=- -=- 411 lines omitted -=- -=- -=-]

+        name = mo_p.group(2).lower()
+        value = mo_p.group(3)
+        l = len(mo_p.group(1))
+    elif mo_q:
+        name = mo_q.group(2).lower()
+        value = mo_q.group(3)
+        l = len(mo_q.group(1))
+    elif mo_unp:
+        name = mo_unp.group(2)
+        l = len(mo_unp.group(1))
+        if result:
+            if parms.has_key(name):
+                if parms[name] is None: raise ParseError, (
+                    'Attribute %s requires a value' % name, tag)
+
+                result[name] = parms[name]
+            else: raise ParseError, (
+                'Invalid attribute name, "%s"' % name, tag)
+        else:
+            result[''] = name
+        return apply(parse_params, (text[l:],result), parms)
+    elif mo_unq:
+        name = mo_unq.group(2)
+        l = len(mo_unq.group(1))
+        if result:
+            raise ParseError, ('Invalid attribute name, "%s"' % name, tag)
+        else:
+            result[''] = name
+        return apply(parse_params, (text[l:], result), parms)
+    else:
+        if not text or not text.strip():
+            return result
+        raise ParseError, ('invalid parameter: "%s"' % text, tag)
+
+    if not parms.has_key(name):
+        raise ParseError, ('Invalid attribute name, "%s"' % name, tag)
+
+    if result.has_key(name):
+        p = parms[name]
+        if type(p) is not ListType or p:
+            raise ParseError, (
+                'Duplicate values for attribute "%s"' % name, tag)
+
+    result[name] = value
+
+    text = text[l:].strip()
+    if text:
+        return apply(parse_params, (text,result), parms)
+    else:
+        return result


=== Zope3/src/zope/documenttemplate/dt_var.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:07 2002
+++ Zope3/src/zope/documenttemplate/dt_var.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,460 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Variable insertion parameters
+
+    When inserting variables, parameters may be specified to
+    control how the data will be formatted.  In HTML source, the
+    'fmt' parameter is used to specify a C-style or custom format
+    to be used when inserting an object.  In EPFS source, the 'fmt'
+    parameter is only used for custom formats, a C-style format is
+    specified after the closing parenthesis.
+
+    Custom formats
+
+       A custom format is used when outputing user-defined
+       objects.  The value of a custom format is a method name to
+       be invoked on the object being inserted.  The method should
+       return an object that, when converted to a string, yields
+       the desired text.  For example, the HTML source::
+
+          <!--#var date fmt=DayOfWeek-->
+
+       Inserts the result of calling the method 'DayOfWeek' of the
+       object bound to the variable 'date', with no arguments.
+
+       In addition to object methods, serveral additional custom
+       formats are available:
+
+           'whole-dollars' -- Show a numeric value with a dollar symbol.
+
+           'dollars-and-cents' -- Show a numeric value with a dollar
+             symbol and two decimal places.
+
+           'collection-length' -- Get the length of a collection of objects.
+
+       Note that when using the EPFS source format, both a
+       C-style and a custom format may be provided.  In this case,
+       the C-Style format is applied to the result of calling
+       the custom formatting method.
+
+    Null values and missing variables
+
+       In some applications, and especially in database applications,
+       data variables may alternate between "good" and "null" or
+       "missing" values.  A format that is used for good values may be
+       inappropriate for null values.  For this reason, the 'null'
+       parameter can be used to specify text to be used for null
+       values.  Null values are defined as values that:
+
+         - Cannot be formatted with the specified format, and
+
+         - Are either the special Python value 'None' or
+           are false and yield an empty string when converted to
+           a string.
+
+       For example, when showing a monitary value retrieved from a
+       database that is either a number or a missing value, the
+       following variable insertion might be used::
+
+           <dtml-var cost fmt="$%.2d" null=\'n/a\'>
+
+       Missing values are providing for variables which are not
+       present in the name space, rather than raising an NameError,
+       you could do this:
+
+           <dtml-var cost missing=0>
+
+       and in this case, if cost was missing, it would be set to 0.
+       In the case where you want to deal with both at the same time,
+       you can use 'default':
+
+           <dtml-var description default=''>
+
+       In this case, it would use '' if the value was null or if the
+       variable was missing.
+
+    String manipulation
+
+       A number of special attributes are provided to transform the
+       value after formatting has been applied.  These parameters
+       are supplied without arguments.
+
+       'lower' --  cause all upper-case letters to be converted to lower case.
+
+       'upper' --  cause all upper-case letters to be converted to lower case.
+
+       'capitalize' -- cause the first character of the inserted value
+       to be converted to upper case.
+
+       'spacify' -- cause underscores in the inserted value to be
+       converted to spaces.
+
+       'thousands_commas' -- cause commas to be inserted every three
+       digits to the left of a decimal point in values containing
+       numbers.  For example, the value, "12000 widgets" becomes
+       "12,000 widgets".
+
+       'html_quote' -- convert characters that have special meaning
+       in HTML to HTML character entities.
+
+       'url_quote' -- convert characters that have special meaning
+       in URLS to HTML character entities using decimal values.
+
+       'url_quote_plus' -- like url_quote but also replace blank
+       space characters with '+'. This is needed for building
+       query strings in some cases.
+
+       'sql_quote' -- Convert single quotes to pairs of single
+       quotes. This is needed to safely include values in
+       Standard Query Language (SQL) strings.
+
+       'newline_to_br' -- Convert newlines and carriage-return and
+       newline combinations to break tags.
+
+       'url' -- Get the absolute URL of the object by calling it\'s
+       'absolute_url' method, if it has one.
+
+    Truncation
+
+       The attributes 'size' and 'etc'  can be used to truncate long
+       strings.  If the 'size' attribute is specified, the string to
+       be inserted is truncated at the given length.  If a space
+       occurs in the second half of the truncated string, then the
+       string is further truncated to the right-most space.  After
+       truncation, the value given for the 'etc' attribute is added to
+       the string.  If the 'etc' attribute is not provided, then '...'
+       is used.  For example, if the value of spam is
+       '"blah blah blah blah"', then the tag
+       '<!--#var spam size=10-->' inserts '"blah blah ..."'.
+
+
+Evaluating expressions without rendering results
+
+   A 'call' tag is provided for evaluating named objects or expressions
+   without rendering the result.
+
+
+$Id$
+"""
+from zope.documenttemplate.dt_util import parse_params, name_param, html_quote, str
+import re, sys
+from urllib import quote, quote_plus
+
+
+class Var:
+    name = 'var'
+    expr = None
+
+    def __init__(self, args, fmt='s'):
+        if args[:4] == 'var ':
+            args = args[4:]
+        args = parse_params(args, name='', lower=1, upper=1, expr='',
+                            capitalize=1, spacify=1, null='', fmt='s',
+                            size=0, etc='...', thousands_commas=1,
+                            html_quote=1, url_quote=1, sql_quote=1,
+                            url_quote_plus=1, missing='',
+                            newline_to_br=1, url=1)
+        self.args = args
+
+        self.modifiers = tuple(
+            map(lambda t: t[1],
+                filter(lambda m, args=args, used=args.has_key:
+                       used(m[0]) and args[m[0]],
+                       modifiers)))
+
+        name, expr = name_param(args, 'var', 1)
+
+        self.__name__, self.expr = name, expr
+        self.fmt = fmt
+
+        if len(args) == 1 and fmt == 's':
+            if expr is None:
+                expr = name
+            else:
+                expr = expr.eval
+            self.simple_form = expr,
+
+
+    def render(self, md):
+        args = self.args
+        have_arg = args.has_key
+        name = self.__name__
+
+        val = self.expr
+
+        if val is None:
+            if md.has_key(name):
+                if have_arg('url'):
+                    val = md.getitem(name,0)
+                    val = val.absolute_url()
+                else:
+                    val = md[name]
+            else:
+                if have_arg('missing'):
+                    return args['missing']
+                else:
+                    raise KeyError, name
+        else:
+            val = val.eval(md)
+            if have_arg('url'):
+                val = val.absolute_url()
+
+        __traceback_info__ = name, val, args
+
+        if have_arg('null') and not val and val != 0:
+            # check for null (false but not zero, including None, [], '')
+            return args['null']
+
+        # handle special formats defined using fmt= first
+        if have_arg('fmt'):
+            fmt=args['fmt']
+            if have_arg('null') and not val and val != 0:
+                try:
+                    if hasattr(val, fmt):
+                        val = getattr(val,fmt)()
+                    elif special_formats.has_key(fmt):
+                        val = special_formats[fmt](val, name, md)
+                    elif fmt == '':
+                        val = ''
+                    else:
+                        val = fmt % val
+                except:
+                    t, v = sys.exc_info()[:2]
+                    if val is None or not str(val):
+                        return args['null']
+                    raise t, v
+
+            else:
+                # We duplicate the code here to avoid exception handler
+                # which tends to screw up stack or leak
+                if hasattr(val, fmt):
+                    val = getattr(val,fmt)()
+                elif special_formats.has_key(fmt):
+                    val = special_formats[fmt](val, name, md)
+                elif fmt == '':
+                    val = ''
+                else:
+                    val = fmt % val
+
+        # finally, pump it through the actual string format...
+        fmt=self.fmt
+        if fmt == 's':
+            val = str(val)
+        else:
+            val = ('%'+self.fmt) % (val,)
+
+        # next, look for upper, lower, etc
+        for f in self.modifiers:
+            val = f(val)
+
+        if have_arg('size'):
+            size = args['size']
+            try:
+                size = int(size)
+            except:
+                raise 'Document Error',(
+                '''a <code>size</code> attribute was used in a <code>var</code>
+                tag with a non-integer value.''')
+            if len(val) > size:
+                val = val[:size]
+                l = val.rfind(' ')
+                if l > size/2:
+                    val = val[:l+1]
+                if have_arg('etc'):
+                    l = args['etc']
+                else:
+                    l = '...'
+                val += l
+
+        return val
+
+    __call__ = render
+
+class Call:
+    name = 'call'
+    expr = None
+
+    def __init__(self, args):
+        args = parse_params(args, name='', expr='')
+        name, expr = name_param(args,'call',1)
+        if expr is None:
+            expr = name
+        else:
+            expr = expr.eval
+        self.simple_form = expr, None
+
+
+def url_quote(v, name='(Unknown name)', md={}):
+    return quote(str(v))
+
+
+def url_quote_plus(v, name='(Unknown name)', md={}):
+    return quote_plus(str(v))
+
+
+def newline_to_br(v, name='(Unknown name)', md={}):
+    v = str(v)
+    if v.find('\r') >= 0:
+        v = ''.join(v.split('\r'))
+    if v.find('\n') >= 0:
+        v = '<br>\n'.join(v.split('\n'))
+    return v
+
+
+def whole_dollars(v, name='(Unknown name)', md={}):
+    try:
+        return "$%d" % v
+    except:
+        return ''
+
+
+def dollars_and_cents(v, name='(Unknown name)', md={}):
+    try:
+        return "$%.2f" % v
+    except:
+        return ''
+
+
+def thousands_commas(v, name='(Unknown name)', md={},
+                     thou=re.compile(
+                         r"([0-9])([0-9][0-9][0-9]([,.]|$))").search):
+
+    v = str(v)
+    vl = v.split('.')
+    if not vl:
+        return v
+    v = vl[0]
+    del vl[0]
+    if vl:
+        s = '.' + '.'.join(vl)
+    else:
+        s = ''
+    mo = thou(v)
+    while mo is not None:
+        l = mo.start(0)
+        v = v[:l+1] + ',' + v[l+1:]
+        mo = thou(v)
+    return v+s
+
+
+def whole_dollars_with_commas(v, name='(Unknown name)', md={}):
+    try:
+        v = "$%d" % v
+    except:
+        v = ''
+    return thousands_commas(v)
+
+
+def dollars_and_cents_with_commas(v, name='(Unknown name)', md={}):
+    try:
+        v = "$%.2f" % v
+    except:
+        v = ''
+    return thousands_commas(v)
+
+
+def len_format(v, name='(Unknown name)', md={}):
+    return str(len(v))
+
+
+def len_comma(v, name='(Unknown name)', md={}):
+    return thousands_commas(str(len(v)))
+
+
+StructuredText=None
+def structured_text(v, name='(Unknown name)', md={}):
+    global StructuredText
+    if StructuredText is None:
+        import StructuredText
+    return str(StructuredText.html_with_references(str(v), 3))
+
+
+def sql_quote(v, name='(Unknown name)', md={}):
+    """Quote single quotes in a string by doubling them.
+
+    This is needed to securely insert values into sql
+    string literals in templates that generate sql.
+    """
+    if v.find("'") >= 0:
+        return "''".join(v.split("'"))
+    return v
+
+
+special_formats={
+    'whole-dollars': whole_dollars,
+    'dollars-and-cents': dollars_and_cents,
+    'collection-length': len_format,
+    # XXX: Gone for now
+    # 'structured-text': structured_text,
+
+    # The rest are depricated:
+    'sql-quote': sql_quote,
+    'html-quote': html_quote,
+    'url-quote': url_quote,
+    'url-quote-plus': url_quote_plus,
+    'multi-line': newline_to_br,
+    'comma-numeric': thousands_commas,
+    'dollars-with-commas': whole_dollars_with_commas,
+    'dollars-and-cents-with-commas': dollars_and_cents_with_commas,
+    }
+
+
+def spacify(val):
+    if val.find('_') >= 0:
+        val = ' '.join(val.split('_'))
+    return val
+
+
+def lower(val):
+    return val.lower()
+
+
+def upper(val):
+    return val.upper()
+
+
+def capitalize(val):
+    return val.capitalize()
+
+
+modifiers = (html_quote, url_quote, url_quote_plus, newline_to_br,
+             lower, upper, capitalize, spacify,
+             thousands_commas, sql_quote)
+modifiers = map(lambda f: (f.__name__, f), modifiers)
+
+
+class Comment:
+    '''Comments
+
+    The 'comment' tag can be used to simply include comments
+    in DTML source.
+
+    For example::
+
+      <dtml-comment>
+
+        This text is not rendered.
+
+      </dtml-comment>
+    '''
+    name = 'comment'
+    blockContinuations = ()
+
+    def __init__(self, args, fmt=''):
+        pass
+
+    def render(self, md):
+        return ''
+
+    __call__=render


=== Zope3/src/zope/documenttemplate/dt_with.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:08 2002
+++ Zope3/src/zope/documenttemplate/dt_with.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,89 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Nested namespace access
+
+   The 'with' tag is used to introduce nested namespaces.
+
+   The text enclosed in the with tag is rendered using information
+   from the given variable or expression.
+
+   For example, if the variable 'person' is bound to an object that
+   has attributes 'name' and 'age', then a 'with' tag like the
+   following can be used to access these attributes::
+
+     <dtml-with person>
+       <dtml-var name>,
+       <dtml-var age>
+     </dtml-with>
+
+   Eather a 'name' or an 'expr' attribute may be used to specify data.
+   A 'mapping' attribute may be used to indicate that the given data
+   should be treated as mapping object, rather than as an object with
+   named attributes.
+
+$Id$
+"""
+
+from zope.documenttemplate.dt_util import parse_params, name_param, InstanceDict, render_blocks, str
+from zope.documenttemplate.dt_util import TemplateDict
+
+from types import StringType, TupleType
+
+
+class With:
+    blockContinuations = ()
+    name = 'with'
+    mapping = None
+    only = 0
+
+    def __init__(self, blocks):
+        tname, args, section = blocks[0]
+        args = parse_params(args, name='', expr='', mapping=1, only=1)
+        name, expr = name_param(args, 'with', 1)
+        if expr is None:
+            expr = name
+        else:
+            expr = expr.eval
+        self.__name__, self.expr = name, expr
+        self.section=section.blocks
+        if args.has_key('mapping') and args['mapping']:
+            self.mapping = 1
+        if args.has_key('only') and args['only']:
+            self.only = 1
+
+    def render(self, md):
+        expr = self.expr
+        if isinstance(expr, StringType):
+            v = md[expr]
+        else:
+            v = expr(md)
+
+        if not self.mapping:
+            if isinstance(v, TupleType) and len(v) == 1:
+                v = v[0]
+            v = InstanceDict(v, md)
+
+        if self.only:
+            _md = md
+            md = TemplateDict()
+            if hasattr(_md, 'validate'):
+                md.validate = _md.validate
+
+        md._push(v)
+        try:
+            return render_blocks(self.section, md)
+        finally:
+            md._pop(1)
+
+    __call__ = render


=== Zope3/src/zope/documenttemplate/pdocumenttemplate.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:08 2002
+++ Zope3/src/zope/documenttemplate/pdocumenttemplate.py	Wed Dec 25 09:13:36 2002
@@ -0,0 +1,239 @@
+##############################################################################
+#
+# 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
+#
+##############################################################################
+"""Python implementations of document template some features
+
+$Id$
+"""
+
+import sys
+from types import StringTypes, TupleType, ClassType
+ClassTypes = [ClassType]
+
+
+def safe_callable(ob):
+    # Works with ExtensionClasses and Acquisition.
+    if hasattr(ob, '__class__'):
+        if hasattr(ob, '__call__'):
+            return 1
+        else:
+            return type(ob) in ClassTypes
+    else:
+        return callable(ob)
+
+
+class InstanceDict:
+
+    def __init__(self, o, namespace):
+        self.self = o
+        self.cache = {}
+        self.namespace = namespace
+
+    def has_key(self,key):
+        return hasattr(self.self,key)
+
+    def keys(self):
+        return self.self.__dict__.keys()
+
+    def __repr__(self):
+        return 'InstanceDict(%s)' % str(self.self)
+
+    def __getitem__(self,key):
+
+        cache=self.cache
+        if cache.has_key(key):
+            return cache[key]
+
+        inst = self.self
+
+        if key[:1] == '_':
+            if key != '__str__':
+                raise KeyError, key # Don't divuldge private data
+            else:
+                return str(inst)
+
+        try:
+            r = getattr(inst, key)
+        except AttributeError:
+            raise KeyError, key
+
+        self.cache[key] = r
+        return r
+
+    def __len__(self):
+        return 1
+
+
+class MultiMapping:
+
+    def __init__(self):
+        self.dicts = []
+
+    def __getitem__(self, key):
+        for d in self.dicts:
+            try:
+                return d[key]
+            except (KeyError, AttributeError):
+                pass
+        raise KeyError, key
+
+    def push(self,d):
+        self.dicts.insert(0, d)
+
+    def pop(self, n=1):
+        r = self.dicts[-1]
+        del self.dicts[:n]
+        return r
+
+    def keys(self):
+        kz = []
+        for d in self.dicts:
+            kz = kz + d.keys()
+        return kz
+
+
+class DictInstance:
+
+    def __init__(self, mapping):
+        self.__d = mapping
+
+    def __getattr__(self, name):
+        try:
+            return self.__d[name]
+        except KeyError:
+            raise AttributeError, name
+
+
+class TemplateDict:
+
+    level = 0
+
+    def _pop(self, n=1):
+        return self.dicts.pop(n)
+
+    def _push(self, d):
+        return self.dicts.push(d)
+
+    def __init__(self):
+        m = self.dicts = MultiMapping()
+        self._pop = m.pop
+        self._push = m.push
+        try:
+            self.keys = m.keys
+        except:
+            pass
+
+    def __getitem__(self,key,call=1):
+
+        v = self.dicts[key]
+        if call:
+            if hasattr(v, '__render_with_namespace__'):
+                return v.__render_with_namespace__(self)
+            vbase = getattr(v, 'aq_base', v)
+            if safe_callable(vbase):
+                v = v()
+        return v
+
+    def __len__(self):
+        total = 0
+        for d in self.dicts.dicts:
+            total += len(d)
+        return total
+
+    def has_key(self,key):
+        try:
+            v=self.dicts[key]
+        except KeyError:
+            return 0
+        return 1
+
+    getitem = __getitem__
+
+    def __call__(self, *args, **kw):
+        if args:
+            if len(args) == 1 and not kw:
+                m=args[0]
+            else:
+                m = self.__class__()
+                for a in args:
+                    m._push(a)
+                if kw:
+                    m._push(kw)
+        else:
+            m=kw
+        return (DictInstance(m),)
+
+
+def render_blocks(blocks, md):
+    rendered = []
+    append = rendered.append
+    for section in blocks:
+        if type(section) is TupleType:
+            l = len(section)
+            if l == 1:
+                # Simple var
+                section = section[0]
+                if isinstance(section, StringTypes):
+                    section = md[section]
+                else:
+                    section = section(md)
+                section = str(section)
+            else:
+                # if
+                cache = {}
+                md._push(cache)
+                try:
+                    i = 0
+                    m = l-1
+                    while i < m:
+                        cond = section[i]
+                        if isinstance(cond, StringTypes):
+                            n = cond
+                            try:
+                                cond = md[cond]
+                                cache[n] = cond
+                            except KeyError, v:
+                                v = v[0]
+                                if n != v:
+                                    raise KeyError, v, sys.exc_traceback
+                                cond=None
+                        else:
+                            cond = cond(md)
+                        if cond:
+                            section = section[i+1]
+                            if section:
+                                section = render_blocks(section,md)
+                            else: section=''
+                            m = 0
+                            break
+                        i += 2
+                    if m:
+                        if i == m:
+                            section = render_blocks(section[i],md)
+                        else:
+                            section = ''
+
+                finally: md._pop()
+
+        elif not isinstance(section, StringTypes):
+            section = section(md)
+
+        if section:
+            rendered.append(section)
+
+    l = len(rendered)
+    if l == 0:
+        return ''
+    elif l == 1:
+        return rendered[0]
+    return ''.join(rendered)
+    return rendered