[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=(
+ (('&'), '&'),
+ (("<"), '<' ),
+ ((">"), '>' ),
+ (('"'), '"'))): #"
+ 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=(
+ (('&'), '&'),
+ (('<'), '<' ),
+ (('>'), '>' ),
+ (('"'), '"'))): #"
+ 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