Commit 62f52e45 authored by Jim Fulton's avatar Jim Fulton

Major redesign of block rendering. The code inside a block tag is

compiled as a template but only the templates blocks are saved, and
later rendered directly with render_blocks.

Added with tag.

Also, for the HTML syntax, we now allow spaces after # and after end
or '/'.  So, the tags::

  <!--#
    with spam
    -->

and::

  <!--#
    end with
    -->

are valid.
parent 1533bb64
......@@ -6,8 +6,16 @@ __doc__='''Comments
The 'comment' tag can be used to simply include comments
in DTML source.
''' # '
__rcs_id__='$Id: DT_Comment.py,v 1.1 1998/03/04 18:19:56 jim Exp $'
For example::
<!--#comment-->
This text is not rendered.
<!--#/comment-->
'''
__rcs_id__='$Id: DT_Comment.py,v 1.2 1998/04/02 17:37:34 jim Exp $'
############################################################################
# Copyright
......@@ -61,7 +69,7 @@ __rcs_id__='$Id: DT_Comment.py,v 1.1 1998/03/04 18:19:56 jim Exp $'
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.1 $'[11:-2]
__version__='$Revision: 1.2 $'[11:-2]
from DT_Util import *
......@@ -81,6 +89,28 @@ class Comment:
############################################################################
# $Log: DT_Comment.py,v $
# Revision 1.2 1998/04/02 17:37:34 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.1 1998/03/04 18:19:56 jim
# added comment and raise tags
#
......
......@@ -55,6 +55,8 @@ Two source formats are supported:
is used to insert the variable 'total' with the C format
'12.2f'.
%(Expr)s
%(Var)s
Document templates support conditional and sequence insertion
......@@ -98,8 +100,6 @@ Access Control
object will have an attribute, AUTHENTICATED_USER that is the
user object that was found if and when Bobo authenticated a user.
%(Expr)s
Document Templates may be created 4 ways:
DocumentTemplate.String -- Creates a document templated from a
......
"""HTML formated DocumentTemplates
$Id: DT_HTML.py,v 1.5 1997/10/27 17:35:32 jim Exp $"""
$Id: DT_HTML.py,v 1.6 1998/04/02 17:37:35 jim Exp $"""
from DT_String import String, FileMixin
import DT_Doc, DT_String, regex
......@@ -12,20 +12,21 @@ from string import strip, find
class dtml_re_class:
def search(self, text, start=0,
name_match=regex.compile('[a-zA-Z]+[\0- ]*').match):
name_match=regex.compile('[\0- ]*[a-zA-Z]+[\0- ]*').match,
end_match=regex.compile('[\0- ]*\(/\|end\)',
regex.casefold).match,
):
s=find(text,'<!--#',start)
if s < 0: return s
e=find(text,'-->',s)
if e < 0: return e
n=s+5
if text[n:n+1]=='/':
end=text[n:n+1]
n=n+1
elif text[n:n+3]=='end':
end=text[n:n+3]
n=n+3
else:
end=''
l=end_match(text,n)
if l > 0:
end=strip(text[n:n+l])
n=n+l
else: end=''
l=name_match(text,n)
if l < 0: return l
......@@ -59,14 +60,6 @@ class HTML(DT_String.String):
def tagre(self):
return dtml_re_class()
return regex.symcomp(
'<!--#' # beginning
'\(<end>/\|end\)?' # end tag marker
'\(<name>[a-z]+\)' # tag name
'[\0- ]*' # space after tag name
'\(<args>\([^>"]+\("[^"]*"\)?\)*\)' # arguments
'-->' # end
, regex.casefold)
def parseTag(self, tagre, command=None, sargs=''):
"""Parse a tag using an already matched re
......@@ -218,6 +211,28 @@ class HTMLFile(FileMixin, HTML):
##########################################################################
#
# $Log: DT_HTML.py,v $
# Revision 1.6 1998/04/02 17:37:35 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.5 1997/10/27 17:35:32 jim
# Removed old validation machinery.
#
......
......@@ -117,8 +117,8 @@ __doc__='''Conditional insertion
# (540) 371-6909
#
############################################################################
__rcs_id__='$Id: DT_If.py,v 1.8 1998/01/14 18:23:42 jim Exp $'
__version__='$Revision: 1.8 $'[11:-2]
__rcs_id__='$Id: DT_If.py,v 1.9 1998/04/02 17:37:35 jim Exp $'
__version__='$Revision: 1.9 $'[11:-2]
from DT_Util import *
import sys
......@@ -135,17 +135,20 @@ class If:
args=parse_params(args, name='', expr='')
name,expr=name_param(args,'if',1)
self.__name__= name
self.sections=[(name, expr, section)]
if expr is None: cond=name
else: cond=expr.eval
sections=[cond, section.blocks]
if blocks[-1][0]=='else':
tname, args, section = blocks[-1]
blocks=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')
self.elses=section
elses=section.blocks
else: elses=None
for tname, args, section in blocks[1:]:
if tname=='else':
......@@ -153,32 +156,14 @@ class If:
'more than one else tag for a single if tag', 'in')
args=parse_params(args, name='', expr='')
name,expr=name_param(args,'elif',1)
self.sections.append((name, expr, section))
def render(self,md):
cache={}
md._push(cache)
try:
for name, expr, section in self.sections:
if expr is None:
try: v=md[name]
except KeyError, ev:
if ev is not name:
raise KeyError, name, sys.exc_traceback
v=None
cache[name]=v
else:
v=expr.eval(md)
if v: return section(None,md)
if self.elses: return self.elses(None, md)
finally: md._pop(1)
return ''
__call__=render
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'
......@@ -188,28 +173,9 @@ class Unless:
tname, args, section = blocks[0]
args=parse_params(args, name='', expr='')
name,expr=name_param(args,'unless',1)
self.__name__ = name
self.section=section
self.expr=expr
def render(self,md):
name=self.__name__
expr=self.expr
if expr is None:
try: v=md[name]
except KeyError, ev:
if ev is not name: raise KeyError, name, sys.exc_traceback
v=None
if not v:
md._push({name:v})
try: return self.section(None,md)
finally: md._pop(1)
else:
if not expr.eval(md): return self.section(None,md)
return ''
__call__=render
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.
......@@ -219,6 +185,28 @@ class Else(Unless):
##########################################################################
#
# $Log: DT_If.py,v $
# Revision 1.9 1998/04/02 17:37:35 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.8 1998/01/14 18:23:42 jim
# Added expr to unless.
#
......
......@@ -296,7 +296,7 @@
''' #'
__rcs_id__='$Id: DT_In.py,v 1.22 1998/03/20 17:52:29 jim Exp $'
__rcs_id__='$Id: DT_In.py,v 1.23 1998/04/02 17:37:36 jim Exp $'
############################################################################
# Copyright
......@@ -350,18 +350,28 @@ __rcs_id__='$Id: DT_In.py,v 1.22 1998/03/20 17:52:29 jim Exp $'
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.22 $'[11:-2]
__version__='$Revision: 1.23 $'[11:-2]
from DT_Util import *
from string import find, atoi, join
import regex
from regsub import gsub
from DT_InSV import sequence_variables, opt
class In:
class InFactory:
blockContinuations=('else',)
name='in'
def __call__(self, blocks):
i=InClass(blocks)
if i.batch: return i.renderwb
else: return i.renderwob
In=InFactory()
class InClass:
elses=None
expr=None
expr=sort=batch=mapping=None
start_name_re=None
def __init__(self, blocks):
......@@ -370,7 +380,23 @@ class In:
orphan='3',overlap='1',mapping=1,
previous=1, next=1, expr='', sort='')
self.args=args
if args.has_key('start'):
has_key=args.has_key
if has_key('sort'): self.sort=args['sort']
if has_key('mapping'): self.mapping=args['mapping']
for n in 'start', 'size', 'end':
if has_key(n): self.batch=1
for n in 'orphan','overlap','previous','next':
if has_key(n) and not self.batch:
raise ParseError, (
"""
The %s attribute was used but neither of the
<code>start</code>, <code>end</code>, or <code>size</code>
attributes were used.
""" % n, 'in')
if has_key('start'):
v=args['start']
if type(v)==type(''):
try: atoi(v)
......@@ -381,8 +407,9 @@ class In:
'=[0-9]+&+')
name,expr=name_param(args,'in',1)
if expr is not None: expr=expr.eval
self.__name__, self.expr = name, expr
self.section=section
self.section=section.blocks
if len(blocks) > 1:
if len(blocks) != 2: raise ParseError, (
'too many else blocks', 'in')
......@@ -393,58 +420,40 @@ class In:
if ename != name:
raise ParseError, (
'name in else does not match in', 'in')
self.elses=section
self.elses=section.blocks
def render(self, md):
def renderwb(self, md):
expr=self.expr
if expr is None: sequence=md[self.__name__]
else: sequence=expr.eval(md)
name=self.__name__
if expr is None:
sequence=md[name]
cache={ name: sequence }
else:
sequence=expr(md)
cache=None
if not sequence:
if self.elses: return self.elses(None, md)
if self.elses: return render_blocks(self.elses, md)
return ''
section=self.section
params=self.args
nbatchparams=len(params)-1
mapping=self.mapping
if params.has_key('mapping'):
mapping=1
nbatchparams=nbatchparams-1
else:
mapping=0
if params.has_key('sort'):
sort=params['sort']
nbatchparams=nbatchparams-1
s=[]
for client in sequence:
if type(client)==TupleType and len(client)==2: v=client[1]
else: v=client
if mapping: v=v[sort]
else: v=getattr(v, sort)
try: v=v()
except: pass
s.append((v,client))
s.sort()
sequence=[]
for v, client in s: sequence.append(client)
if self.sort is not None: sequence=self.sort_sequence(sequence)
next=previous=0
if nbatchparams:
try: start=int_param(params,md,'start',0)
except: start=1
end=int_param(params,md,'end',0)
size=int_param(params,md,'size',0)
overlap=int_param(params,md,'overlap',0)
orphan=int_param(params,md,'orphan','3')
start,end,sz=opt(start,end,size,orphan,sequence)
if params.has_key('next'): next=1
if params.has_key('previous'): previous=1
else:
start=1
end=len(sequence)
try: start=int_param(params,md,'start',0)
except: start=1
end=int_param(params,md,'end',0)
size=int_param(params,md,'size',0)
overlap=int_param(params,md,'overlap',0)
orphan=int_param(params,md,'orphan','3')
start,end,sz=opt(start,end,size,orphan,sequence)
if params.has_key('next'): next=1
if params.has_key('previous'): previous=1
last=end-1
first=start-1
......@@ -454,18 +463,22 @@ class In:
vars=sequence_variables(sequence,'?'+query_string,self.start_name_re)
kw=vars.data
if expr is None: kw[self.__name__]=sequence # Cache sequence
kw['mapping']=mapping
if nbatchparams:
kw['sequence-step-size']=sz
kw['sequence-step-overlap']=overlap
kw['sequence-step-start']=start
kw['sequence-step-end']=end
kw['sequence-step-start-index']=start-1
kw['sequence-step-end-index']=end-1
kw['sequence-step-orphan']=orphan
kw['sequence-step-size']=sz
kw['sequence-step-overlap']=overlap
kw['sequence-step-start']=start
kw['sequence-step-end']=end
kw['sequence-step-start-index']=start-1
kw['sequence-step-end-index']=end-1
kw['sequence-step-orphan']=orphan
push=md._push
pop=md._pop
render=render_blocks
if cache: push(cache)
push(vars)
try:
md._push(vars)
if previous:
if first > 0:
pstart,pend,psize=opt(None,first+overlap,
......@@ -474,13 +487,13 @@ class In:
kw['previous-sequence-start-index']=pstart-1
kw['previous-sequence-end-index']=pend-1
kw['previous-sequence-size']=pend+1-pstart
result=section(None,md)
result=render(section,md)
elif self.elses: result=self.elses(None, md)
elif self.elses: result=render(self.elses, md)
else: result=''
elif next:
try:
# The following line is a neaky way to test whether
# The following line is a sneaky way to test whether
# there are more items, without actually
# computing a length:
sequence[end]
......@@ -490,60 +503,154 @@ class In:
kw['next-sequence-start-index']=pstart-1
kw['next-sequence-end-index']=pend-1
kw['next-sequence-size']=pend+1-pstart
result=section(None,md)
result=render(section,md)
except:
if self.elses: result=self.elses(None, md)
if self.elses: result=render(self.elses, md)
else: result=''
else:
result = []
for index in range(first,end):
if nbatchparams:
if index==first and index > 0:
pstart,pend,psize=opt(None,index+overlap,
sz,orphan,sequence)
kw['previous-sequence']=1
kw['previous-sequence-start-index']=pstart-1
kw['previous-sequence-end-index']=pend-1
kw['previous-sequence-size']=pend+1-pstart
else:
kw['previous-sequence']=0
if index==last:
try:
# The following line is a neaky way to
# test whether there are more items,
# without actually computing a length:
sequence[end]
pstart,pend,psize=opt(end+1-overlap,None,
sz,orphan,sequence)
kw['previous-sequence']=0
kw['next-sequence']=1
kw['next-sequence-start-index']=pstart-1
kw['next-sequence-end-index']=pend-1
kw['next-sequence-size']=pend+1-pstart
except: pass
result = []
append=result.append
for index in range(first,end):
if index==first and index > 0:
pstart,pend,psize=opt(None,index+overlap,
sz,orphan,sequence)
kw['previous-sequence']=1
kw['previous-sequence-start-index']=pstart-1
kw['previous-sequence-end-index']=pend-1
kw['previous-sequence-size']=pend+1-pstart
else:
kw['previous-sequence']=0
if index==last:
try:
# The following line is a sneaky way to
# test whether there are more items,
# without actually computing a length:
sequence[end]
pstart,pend,psize=opt(end+1-overlap,None,
sz,orphan,sequence)
kw['previous-sequence']=0
kw['next-sequence']=1
kw['next-sequence-start-index']=pstart-1
kw['next-sequence-end-index']=pend-1
kw['next-sequence-size']=pend+1-pstart
except: pass
if index==first: kw['sequence-start']=1
else: kw['sequence-start']=0
if index==last: kw['sequence-end']=1
else: kw['sequence-end']=0
client=sequence[index]
kw['sequence-index']=index
kw['sequence-index-even']=kw['sequence-index-is-even']=\
index % 2
kw['sequence-index-odd']=kw['sequence-index-is-odd']=\
not (index % 2)
if type(client)==TupleType and len(client)==2:
client=client[1]
if mapping:
client=mapping_wrapper(client)
result.append(section(client,md))
if mapping: push(client)
else: push(InstanceDict(client, md))
try: append(render(section, md))
finally: pop(1)
if index==first: kw['sequence-start']=0
result=join(result, '')
finally:
md._pop(1)
if cache: pop()
pop()
return result
__call__=render
def renderwob(self, md):
expr=self.expr
name=self.__name__
if expr is None:
sequence=md[name]
cache={ name: sequence }
else:
sequence=expr(md)
cache=None
if not sequence:
if self.elses: return render_blocks(self.elses, md)
return ''
section=self.section
mapping=self.mapping
if self.sort is not None: sequence=self.sort_sequence(sequence)
vars=sequence_variables(sequence)
kw=vars.data
kw['mapping']=mapping
l=len(sequence)
last=l-1
push=md._push
pop=md._pop
render=render_blocks
if cache: push(cache)
push(vars)
try:
result = []
append=result.append
for index in range(l):
if index==last: kw['sequence-end']=1
client=sequence[index]
kw['sequence-index']=index
if type(client)==TupleType and len(client)==2:
client=client[1]
if mapping: push(client)
else: push(InstanceDict(client, md))
try: append(render(section, md))
finally: pop()
if index==0: kw['sequence-start']=0
result=join(result, '')
finally:
if cache: pop()
pop()
return result
def sort_sequence(self, sequence):
sort=self.sort
mapping=self.mapping
isort=not sort
k=None
s=[]
for client in sequence:
if type(client)==TupleType and len(client)==2:
if isort: k=client[0]
v=client[1]
else:
if isort: k=client
v=client
if sort:
if mapping: k=v[sort]
else: k=getattr(v, sort)
if not basic_type(k):
try: k=k()
except: pass
s.append((k,client))
s.sort()
sequence=[]
for k, client in s: sequence.append(client)
return sequence
basic_type={type(''): 1, type(0): 1, type(0.0): 1, type(()): 1, type([]): 1
}.has_key
def int_param(params,md,name,default=0):
try: v=params[name]
......@@ -556,324 +663,30 @@ def int_param(params,md,name,default=0):
v=atoi(v)
return v
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 start > l: start=l
if end > 0:
if end > start: end=start
else:
end=start+size-1
try: sequence[end+orphan-1]
except: end=len(sequence)
# if l - end < orphan: end=l
elif end > 0:
try: sequence[end-1]
except: end=len(sequence)
# if end > l: end=l
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)
# if l - end < orphan: end=l
return start,end,size
class mapping_wrapper:
def __init__(self,mapping):
self.mapping=mapping
def __getattr__(self,name):
return self.mapping[name]
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={
'previous-sequence': 0,
'next-sequence': 0,
}
def number(self,index): return index+1
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=type(())):
i=self.items[index]
if type(i) is tt and len(i)==2: return i[1]
return i
def roman(self,index): return lower(self.Roman(index))
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 type(item)==TupleType and len(item)==2:
item=item[1]
if data['mapping']: return item[name]
return getattr(item,name)
def first(self,name):
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):
data=self.data
if data['sequence-end']: return 1
index=data['sequence-index']
return self.value(index,name) != self.value(index+1,name)
statistic_names=(
'total', 'count', 'min', 'max', 'median', 'mean',
'variance', 'variance-n','standard-deviation', 'standard-deviation-n',
)
def statistics(self,name):
try:
import Missing
mv=Missing.Value
except: mv=None
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:
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]=math.sqrt(sumsq)
if count > 1:
sumsq=sumsq*n/(n-1)
data['variance-%s' % name]=sumsq
data['standard-deviation-%s' % name]=math.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
def __getitem__(self,key):
data=self.data
try: return data[key]
except:
if key=='previous-batches':
return self.previous_batches()
if key=='next-batches':
return self.next_batches()
l=rfind(key,'-')
suffix=key[l+1:]
prefix=key[:l]
try: return getattr(self,suffix)(data[prefix+'-index'])
except:
if prefix[-4:]=='-var':
prefix=prefix[:-4]
try: return self.value(data[prefix+'-index'],suffix)
except: pass
elif prefix in self.statistic_names:
self.statistics(suffix)
return data[key]
elif prefix=='first': return self.first(suffix)
elif prefix=='last': return self.last(suffix)
elif key=='sequence-length':
data[key]=l=len(self.items)
return l
elif key=='sequence-query' and self.start_name_re is not None:
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(query_string)
if l >= 0:
v=re.group(0)
query_string=(query_string[:l]+
query_string[l+len(v)-1:])
query_string='?'+query_string[1:]
else: query_string='?'
data[key]=query_string
return query_string
raise KeyError, key
def next_batches(self):
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,None,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):
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(None,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
############################################################################
# $Log: DT_In.py,v $
# Revision 1.23 1998/04/02 17:37:36 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.22 1998/03/20 17:52:29 jim
# Added new meaning for else tag when next or previous are used.
#
......
#!/bin/env python
##############################################################################
#
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved. Copyright in this software is owned by DCLC,
# unless otherwise indicated. Permission to use, copy and
# distribute this software is hereby granted, provided that the
# above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear. Note that
# any product, process or technology described in this software
# may be the subject of other Intellectual Property rights
# reserved by Digital Creations, L.C. and are not licensed
# hereunder.
#
# Trademarks
#
# Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
# All other trademarks are owned by their respective companies.
#
# No Warranty
#
# The software is provided "as is" without warranty of any kind,
# either express or implied, including, but not limited to, the
# implied warranties of merchantability, fitness for a particular
# purpose, or non-infringement. This software could include
# technical inaccuracies or typographical errors. Changes are
# periodically made to the software; these changes will be
# incorporated in new editions of the software. DCLC may make
# improvements and/or changes in this software at any time
# without notice.
#
# Limitation Of Liability
#
# In no event will DCLC be liable for direct, indirect, special,
# incidental, economic, cover, or consequential damages arising
# out of the use of or inability to use this software even if
# advised of the possibility of such damages. Some states do not
# allow the exclusion or limitation of implied warranties or
# limitation of liability for incidental or consequential
# damages, so the above limitation or exclusion may not apply to
# you.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
__doc__='''Sequence variables support
$Id: DT_InSV.py,v 1.1 1998/04/02 17:37:36 jim Exp $'''
__version__='$Revision: 1.1 $'[11:-2]
from string import lower, upper, rfind
from math import sqrt
TupleType=type(())
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 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=type(())):
i=self.items[index]
if type(i) is tt and len(i)==2: return i[1]
return i
def roman(self,index): return lower(self.Roman(index))
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 type(item)==TupleType and len(item)==2:
item=item[1]
if data['mapping']: return item[name]
return getattr(item,name)
def first(self,name):
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):
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(query_string)
if l >= 0:
v=re.group(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):
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:
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
def next_batches(self, suffix='batches'):
if suffix != 'batches': raise KeyError, 'next-batches'
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,None,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'):
if suffix != 'batches': raise KeyError, 'previous-batches'
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(None,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: self['sequence-'+suffix],
'sequence-index-is': lambda self, suffix: 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=rfind(key,'-')
if l < 0: raise KeyError, key
suffix=key[l+1:]
prefix=key[:l]
if hasattr(self, suffix):
return getattr(self,suffix)(data[prefix+'-index'])
if special_prefix(prefix):
return special_prefixes[prefix](self, suffix)
if prefix[-4:]=='-var':
prefix=prefix[:-4]
try: return self.value(data[prefix+'-index'],suffix)
except: pass
raise KeyError, key
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 start > l: start=l
if end > 0:
if end > start: end=start
else:
end=start+size-1
try: sequence[end+orphan-1]
except: end=len(sequence)
# if l - end < orphan: end=l
elif end > 0:
try: sequence[end-1]
except: end=len(sequence)
# if end > l: end=l
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)
# if l - end < orphan: end=l
return start,end,size
##############################################################################
#
# $Log: DT_InSV.py,v $
# Revision 1.1 1998/04/02 17:37:36 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
#
from string import *
import DT_Doc, DT_Var, DT_In, DT_If, regex, DT_Raise
import DT_Doc, DT_Var, DT_In, DT_If, regex, DT_Raise, DT_With
Var=DT_Var.Var
from DT_Util import *
......@@ -28,6 +28,7 @@ class String:
'var': DT_Var.Var,
'call': DT_Var.Call,
'in': DT_In.In,
'with': DT_With.With,
'if': DT_If.If,
'unless': DT_If.Unless,
'else': DT_If.Else,
......@@ -108,10 +109,10 @@ class String:
tag, l, args, command)
else:
try:
if command is Var:
result.append(command(args, self.varExtra(tagre)))
else:
result.append(command(args))
if command is Var: r=command(args, self.varExtra(tagre))
else: r=command(args)
if hasattr(r,'simple_form'): r=r.simple_form
result.append(r)
except ParseError, m: self.parse_error(m[0],tag,text,l)
l=tagre.search(text,start)
......@@ -166,8 +167,9 @@ class String:
sstart=start
else:
try:
if scommand is not Comment:
result.append(scommand(blocks))
r=scommand(blocks)
if hasattr(r,'simple_form'): r=r.simple_form
result.append(r)
except ParseError, m: self.parse_error(m[0],stag,text,l)
return start
......@@ -365,7 +367,7 @@ class String:
md.level=level+1
if client is not None:
push(InstanceDict(client,md,self.validate)) # Circ. Ref. 8-|
push(InstanceDict(client,md)) # Circ. Ref. 8-|
pushed=pushed+1
if self._vars:
......@@ -377,7 +379,7 @@ class String:
pushed=pushed+1
try:
return render_blocks(self,md)
return render_blocks(self.blocks,md)
finally:
if pushed: md._pop(pushed) # Get rid of circular reference!
md.level=level # Restore previous level
......
'''$Id: DT_Util.py,v 1.30 1998/03/26 22:02:16 jim Exp $'''
'''$Id: DT_Util.py,v 1.31 1998/04/02 17:37:37 jim Exp $'''
############################################################################
# Copyright
......@@ -52,7 +52,7 @@
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.30 $'[11:-2]
__version__='$Revision: 1.31 $'[11:-2]
import sys, regex, string, types, math, os
from string import rfind, strip, joinfields, atoi,lower,upper,capitalize
......@@ -327,8 +327,32 @@ def parse_params(text,
try: from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks
except: from pDocumentTemplate import InstanceDict, TemplateDict, render_blocks
#from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks
############################################################################
# $Log: DT_Util.py,v $
# Revision 1.31 1998/04/02 17:37:37 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.30 1998/03/26 22:02:16 jim
# Changed value of ValidationError to Unauthorized.
#
......
......@@ -105,8 +105,16 @@ __doc__='''Variable insertion parameters
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.
''' # '
__rcs_id__='$Id: DT_Var.py,v 1.11 1998/03/24 20:21:39 jim Exp $'
__rcs_id__='$Id: DT_Var.py,v 1.12 1998/04/02 17:37:38 jim Exp $'
############################################################################
# Copyright
......@@ -160,7 +168,7 @@ __rcs_id__='$Id: DT_Var.py,v 1.11 1998/03/24 20:21:39 jim Exp $'
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.11 $'[11:-2]
__version__='$Revision: 1.12 $'[11:-2]
from DT_Util import *
......@@ -189,6 +197,11 @@ class Var:
self.__name__, self.expr = name, expr
self.fmt = fmt
if len(args)==1:
if expr is None: expr=name
else: expr=expr.eval
self.simple_form=expr,
def render(self, md):
name=self.__name__
val=self.expr
......@@ -259,16 +272,10 @@ class Call:
def __init__(self, args):
args = parse_params(args, name='', expr='')
self.__name__, self.expr = name_param(args,'call',1)
name, expr = name_param(args,'call',1)
if expr is None: expr=None
self.simple_form=expr,None
def render(self, md):
name=self.__name__
val=self.expr
if val is None: md[name]
else: val.eval(md)
return ''
__call__=render
def html_quote(v, name='(Unknown name)', md={},
character_entities=(
......@@ -360,6 +367,28 @@ modifiers=map(lambda f: (f.__name__, f), modifiers)
############################################################################
# $Log: DT_Var.py,v $
# Revision 1.12 1998/04/02 17:37:38 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.11 1998/03/24 20:21:39 jim
# Added 'call' tag.
#
......
'''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::
<!--#with person-->
<!--#var name-->,
<!--#var age-->
<!--#/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.
'''
__rcs_id__='$Id: DT_With.py,v 1.1 1998/04/02 17:37:38 jim Exp $'
############################################################################
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved. Copyright in this software is owned by DCLC,
# unless otherwise indicated. Permission to use, copy and
# distribute this software is hereby granted, provided that the
# above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear. Note that
# any product, process or technology described in this software
# may be the subject of other Intellectual Property rights
# reserved by Digital Creations, L.C. and are not licensed
# hereunder.
#
# Trademarks
#
# Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
# All other trademarks are owned by their respective companies.
#
# No Warranty
#
# The software is provided "as is" without warranty of any kind,
# either express or implied, including, but not limited to, the
# implied warranties of merchantability, fitness for a particular
# purpose, or non-infringement. This software could include
# technical inaccuracies or typographical errors. Changes are
# periodically made to the software; these changes will be
# incorporated in new editions of the software. DCLC may make
# improvements and/or changes in this software at any time
# without notice.
#
# Limitation Of Liability
#
# In no event will DCLC be liable for direct, indirect, special,
# incidental, economic, cover, or consequential damages arising
# out of the use of or inability to use this software even if
# advised of the possibility of such damages. Some states do not
# allow the exclusion or limitation of implied warranties or
# limitation of liability for incidental or consequential
# damages, so the above limitation or exclusion may not apply to
# you.
#
#
# If you have questions regarding this software,
# contact:
#
# Jim Fulton, jim@digicool.com
#
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.1 $'[11:-2]
from DT_Util import *
class With:
blockContinuations=()
name='with'
mapping=None
def __init__(self, blocks):
tname, args, section = blocks[0]
args=parse_params(args, name='', expr='', mapping=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
def render(self, md):
expr=self.expr
if type(expr) is type(''): v=md[expr]
else: v=expr(md)
if self.mapping: md._push(v)
else:
if type(v) is type(()) and len(v)==1: v=v[0]
md._push(InstanceDict(v,md))
try: return render_blocks(self.section, md)
finally: md._pop(1)
__call__=render
......@@ -2,7 +2,7 @@
"""Document Template Tests
"""
__rcs_id__='$Id: DTtest.py,v 1.3 1997/11/11 18:39:00 jim Exp $'
__rcs_id__='$Id: DTtest.py,v 1.4 1998/04/02 17:37:38 jim Exp $'
############################################################################
# Copyright
......@@ -56,7 +56,7 @@ __rcs_id__='$Id: DTtest.py,v 1.3 1997/11/11 18:39:00 jim Exp $'
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.3 $'[11:-2]
__version__='$Revision: 1.4 $'[11:-2]
from DocumentTemplate import *
import sys
......@@ -121,7 +121,7 @@ def test1():
print ss(aa)
print 'num inaccessible:'
ss.names({'args':'args'})
# ss.names({'args':'args'})
print ss(aa)
print 'quoted source:'
......@@ -408,11 +408,11 @@ def test8():
def test9():
html=HTML(
"""
<!--#in spam-->
<!--#in sequence-item-->
<!--#var sequence-item-->
<!--#/in sequence-item-->
<!--#/in spam-->
<!--#in spam-->
<!--#in sequence-item-->
<!--#var sequence-item-->
<!--#/in sequence-item-->
<!--#/in spam-->
""")
print html(spam=[[1,2,3],[4,5,6]])
......@@ -506,12 +506,6 @@ def main():
print 'Test 6', '='*60
try: test6()
except: traceback.print_exc()
print 'Test 7', '='*60
try: test7()
except: traceback.print_exc()
print 'Test 8', '='*60
try: test8()
except: traceback.print_exc()
print 'Test 9', '='*60
try: test9()
except: traceback.print_exc()
......@@ -540,6 +534,28 @@ if __name__ == "__main__":
############################################################################
# $Log: DTtest.py,v $
# Revision 1.4 1998/04/02 17:37:38 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.3 1997/11/11 18:39:00 jim
# Added test for:
# Made sequence-items work when iterating over mapping items.
......
#!/usr/local/bin/python
# $What$
import DT_Doc, DT_Var, DT_In, DT_If, DT_Util
import DT_Doc, DT_Var, DT_In, DT_If, DT_Util, DT_Comment, DT_Raise, DT_With
__doc__=DT_Doc.__doc__ % {
'In': DT_In.__doc__,
'If': DT_If.__doc__,
'Var': DT_Var.__doc__,
'Expr': DT_Util.Expr_doc,
'id': '$Id: DocumentTemplate.py,v 1.4 1997/10/29 22:06:32 jim Exp $'
'Comment': DT_Comment.__doc__,
'Raise': DT_Raise.__doc__,
'With': DT_With.__doc__,
'id': '$Id: DocumentTemplate.py,v 1.5 1998/04/02 17:37:39 jim Exp $'
}
############################################################################
......@@ -62,7 +65,7 @@ __doc__=DT_Doc.__doc__ % {
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.4 $'[11:-2]
__version__='$Revision: 1.5 $'[11:-2]
ParseError='Document Template Parse Error'
......@@ -73,6 +76,28 @@ from DT_Var import html_quote
############################################################################
# $Log: DocumentTemplate.py,v $
# Revision 1.5 1998/04/02 17:37:39 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.4 1997/10/29 22:06:32 jim
# Cleaned up imports.
#
......
"""Very Safe Python Expressions
"""
__rcs_id__='$Id: VSEval.py,v 1.11 1998/03/12 21:37:01 jim Exp $'
__rcs_id__='$Id: VSEval.py,v 1.12 1998/04/02 17:37:39 jim Exp $'
############################################################################
# Copyright
......@@ -11,7 +11,7 @@ __rcs_id__='$Id: VSEval.py,v 1.11 1998/03/12 21:37:01 jim Exp $'
# rights reserved.
#
############################################################################
__version__='$Revision: 1.11 $'[11:-2]
__version__='$Revision: 1.12 $'[11:-2]
from string import join, find, split, translate
import sys, gparse, string
......@@ -73,6 +73,7 @@ class Eval:
globals -- A global namespace.
"""
self.__name__=expr
expr=translate(expr,nltosp)
self.expr=expr
self.globals=globals
......@@ -131,6 +132,28 @@ compiled_getattr=compile(
############################################################################
#
# $Log: VSEval.py,v $
# Revision 1.12 1998/04/02 17:37:39 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.11 1998/03/12 21:37:01 jim
# Added _getattr.
#
......
/***********************************************************
/******************************************************************
Copyright
Copyright 1997 Digital Creations, L.L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved.
******************************************************************/
******************************************************************/
static char cDocumentTemplate_module_documentation[] =
""
"\n$Id: cDocumentTemplate.c,v 1.10 1998/03/26 21:55:40 jim Exp $"
"\n$Id: cDocumentTemplate.c,v 1.11 1998/04/02 17:37:40 jim Exp $"
;
#include "ExtensionClass.h"
static PyObject *py_isDocTemp=0, *py_blocks=0, *py_=0, *join=0, *py_acquire;
static PyObject *py___call__;
static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized;
static PyObject *py_Unauthorized_fmt, *py_validate;
static PyObject *py__push, *py__pop;
/* ----------------------------------------------------- */
......@@ -36,16 +39,22 @@ typedef struct {
staticforward PyExtensionClass InstanceDictType;
static PyObject *
InstanceDict___init__( InstanceDictobject *self, PyObject *args)
InstanceDict___init__(InstanceDictobject *self, PyObject *args)
{
UNLESS(PyArg_ParseTuple(args, "OOO",
self->validate=NULL;
UNLESS(PyArg_ParseTuple(args, "OO|O",
&(self->inst),
&(self->namespace),
&(self->validate)))
return NULL;
Py_INCREF(self->inst);
Py_INCREF(self->namespace);
Py_INCREF(self->validate);
if(self->validate)
Py_INCREF(self->validate);
else
UNLESS(self->validate=PyObject_GetAttr(self->namespace, py_validate))
return NULL;
UNLESS(self->cache=PyDict_New()) return NULL;
Py_INCREF(Py_None);
return Py_None;
......@@ -291,7 +300,8 @@ MM_cget(MM *self, PyObject *key, int call)
if(dt)
{
ASSIGN(e,PyObject_CallFunction(e,"OO", Py_None, self));
UNLESS(e) return NULL;
UNLESS(e)
return NULL;
}
else
{
......@@ -493,62 +503,353 @@ static struct PyMethodDef TemplateDict_methods[] = {
/* List of methods defined in the module */
static PyObject *
render_blocks(PyObject *self, PyObject *args)
static int
if_finally(PyObject *md, int err)
{
PyObject *md, *blocks, *rendered, *block;
int l, i, k;
PyObject *t, *v, *tb;
UNLESS(PyArg_ParseTuple(args,"OO", &self, &md)) return NULL;
UNLESS(md=Py_BuildValue("(O)",md)) return NULL;
UNLESS(rendered=PyList_New(0)) goto err;
UNLESS(blocks=PyObject_GetAttr(self,py_blocks)) goto err;
if((l=PyList_Size(blocks)) < 0) goto err;
if(err) PyErr_Fetch(&t, &v, &tb);
md=PyObject_GetAttr(md,py__pop);
if(md) ASSIGN(md, PyObject_CallObject(md,NULL));
if(err) PyErr_Restore(t,v,tb);
if(md)
{
Py_DECREF(md);
return -1;
}
else
return -2;
}
static int
render_blocks_(PyObject *blocks, PyObject *rendered,
PyObject *md, PyObject *mda)
{
PyObject *block;
int l, i, k=0, append;
if((l=PyList_Size(blocks)) < 0) return -1;
for(i=0; i < l; i++)
{
block=PyList_GET_ITEM(((PyListObject*)blocks), i);
if(PyString_Check(block))
append=1;
if(PyTuple_Check(block))
{
if(PyList_Append(rendered,block) < 0) goto err;
int bs;
bs=((PyTupleObject*)block)->ob_size;
if(bs==1)
{
/* Simple var */
block=PyTuple_GET_ITEM(block,0);
if(PyString_Check(block)) block=PyObject_GetItem(md,block);
else block=PyObject_CallObject(block,mda);
if(block) ASSIGN(block, PyObject_Str(block));
UNLESS(block) return -1;
}
else
{
/* if */
int icond, m;
PyObject *cond, *n, *cache;
UNLESS(cache=PyDict_New()) return -1;
cond=PyObject_GetAttr(md,py__push);
if(cond) ASSIGN(cond, PyObject_CallFunction(cond,"O",cache));
Py_DECREF(cache);
if(cond) Py_DECREF(cond);
else return -1;
append=0;
m=bs-1;
for(icond=0; icond < m; icond += 2)
{
cond=PyTuple_GET_ITEM(block,icond);
if(PyString_Check(cond))
{
/* We have to be careful to handle key errors here */
n=cond;
if(cond=PyObject_GetItem(md,cond))
{
if(PyDict_SetItem(cache, n, cond) < 0)
{
Py_DECREF(cond);
return if_finally(md,1);
}
}
else
{
PyObject *t, *v, *tb;
PyErr_Fetch(&t, &v, &tb);
if(t != PyExc_KeyError || PyObject_Compare(v,n))
{
PyErr_Restore(t,v,tb);
return if_finally(md,1);
}
Py_XDECREF(t);
Py_XDECREF(v);
Py_XDECREF(tb);
cond=Py_None;
Py_INCREF(cond);
}
}
else
UNLESS(cond=PyObject_CallObject(cond,mda))
return if_finally(md,1);
if(PyObject_IsTrue(cond))
{
Py_DECREF(cond);
block=PyTuple_GET_ITEM(block,icond+1);
if(block!=Py_None &&
render_blocks_(block, rendered, md, mda) < 0)
return if_finally(md,1);
m=-1;
break;
}
else Py_DECREF(cond);
}
if(icond==m)
{
block=PyTuple_GET_ITEM(block,icond);
if(block!=Py_None &&
render_blocks_(block, rendered, md, mda) < 0)
return if_finally(md,1);
}
if(if_finally(md,0) == -2) return -1;
}
}
else if(PyString_Check(block))
{
Py_INCREF(block);
}
else
{
UNLESS(block=PyObject_CallObject(block,md)) goto err;
UNLESS(block=PyObject_CallObject(block,mda)) return -1;
}
if(append && PyObject_IsTrue(block))
{
k=PyList_Append(rendered,block);
Py_DECREF(block);
if(k < 0) goto err;
if(k < 0) return -1;
}
}
Py_DECREF(md);
Py_DECREF(blocks);
ASSIGN(rendered,PyObject_CallFunction(join,"OO",rendered,py_));
return 0;
}
static PyObject *
render_blocks(PyObject *self, PyObject *args)
{
PyObject *md, *blocks, *mda=0, *rendered=0;
int l;
UNLESS(PyArg_ParseTuple(args,"OO", &blocks, &md)) return NULL;
UNLESS(rendered=PyList_New(0)) goto err;
UNLESS(mda=Py_BuildValue("(O)",md)) goto err;
if(render_blocks_(blocks, rendered, md, mda) < 0) goto err;
Py_DECREF(mda);
l=PyList_Size(rendered);
if(l==0)
{
Py_INCREF(py_);
ASSIGN(rendered, py_);
}
else if(l==1)
ASSIGN(rendered, PySequence_GetItem(rendered,0));
else
ASSIGN(rendered, PyObject_CallFunction(join,"OO",rendered,py_));
return rendered;
err:
Py_DECREF(md);
Py_XDECREF(mda);
Py_XDECREF(rendered);
Py_XDECREF(blocks);
return NULL;
}
}
static struct PyMethodDef Module_Level__methods[] = {
{"render_blocks", (PyCFunction)render_blocks, METH_VARARGS,
""},
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
};
static PyObject *
validate(PyObject *self, PyObject *args)
{
PyObject *inst, *parent, *name, *value, *md, *__roles__;
/* def validate(self, inst, parent, name, value, md): */
UNLESS(PyArg_ParseTuple(args,"OOOOO",&inst,&parent,&name,&value,&md))
return NULL;
/*
if hasattr(value, '__roles__'):
roles=value.__roles__
elif inst is parent:
return 1
else:
# if str(name)[:6]=='manage': return 0
if hasattr(parent,'__roles__'):
roles=parent.__roles__
elif hasattr(parent, 'aq_acquire'):
try: roles=parent.aq_acquire('__roles__')
except AttributeError: return 0
else: return 0
value=parent
*/
UNLESS(__roles__=PyObject_GetAttr(value,py___roles__))
{
PyErr_Clear();
if(inst==parent) return PyInt_FromLong(1);
UNLESS(__roles__=PyObject_GetAttr(parent,py___roles__))
{
PyErr_Clear();
UNLESS(__roles__=PyObject_GetAttr(parent,py_acquire)) goto err0;
ASSIGN(__roles__,PyObject_CallFunction(__roles__,"O",py___roles__));
UNLESS(__roles__) goto err0;
value=parent;
}
}
/* if roles is None: return 1 */
if(__roles__==Py_None)
{
Py_DECREF(__roles__);
return PyInt_FromLong(1);
}
/* try:
if md.AUTHENTICATED_USER.hasRole(value, roles):
return 1
except AttributeError: pass
*/
if(md=PyObject_GetAttr(md,py_AUTHENTICATED_USER))
{
ASSIGN(md,PyObject_GetAttr(md,py_hasRole));
if(md) ASSIGN(md,PyObject_CallFunction(md,"OO",value,__roles__));
if(md)
{
if(PyObject_IsTrue(md))
{
Py_DECREF(__roles__);
return md;
}
Py_DECREF(md);
}
else PyErr_Clear();
}
else PyErr_Clear();
/* for r in self._proxy_roles:
if r in roles: return 1
*/
if(PyObject_IsTrue(__roles__))
if((md=PyObject_GetAttr(self, py__proxy_roles)))
{
int i,l, isIn;
PyObject *role;
if((l=PyObject_Length(md)) < 0) PyErr_Clear();
else
{
for(i=0; i < l; i++)
{
UNLESS(role=PySequence_GetItem(md,i))
{
PyErr_Clear();
break;
}
isIn=PySequence_In(__roles__,role);
Py_DECREF(role);
if(isIn < 0)
{
PyErr_Clear();
break;
}
if(isIn)
{
Py_DECREF(md);
return __roles__; /* Any true object would do. */
}
}
}
Py_DECREF(md);
}
else PyErr_Clear();
Py_DECREF(__roles__);
/* if inst is parent:
raise 'Unauthorized', (
'You are not authorized to access <em>%s</em>.' % name)
*/
if(inst==parent)
{
if(name=PyString_Format(py_Unauthorized_fmt, name))
{
PyErr_SetObject(py_Unauthorized, name);
Py_DECREF(name);
}
return NULL;
}
/* return 0 */
return PyInt_FromLong(0);
err0:
PyErr_Clear();
return PyInt_FromLong(0);
}
static struct PyMethodDef Document_methods[] = {
{"validate", (PyCFunction)validate, METH_VARARGS,
""},
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initcDocumentTemplate) */
void
initcDocumentTemplate()
{
PyObject *m, *d;
char *rev="$Revision: 1.10 $";
char *rev="$Revision: 1.11 $";
PURE_MIXIN_CLASS(cDocument,
"Base class for documents that adds fast validation method",
Document_methods);
UNLESS(py_isDocTemp=PyString_FromString("isDocTemp")) return;
UNLESS(py_blocks=PyString_FromString("blocks")) return;
UNLESS(py_acquire=PyString_FromString("aq_acquire")) return;
UNLESS(py___call__=PyString_FromString("__call__")) return;
UNLESS(py___roles__=PyString_FromString("__roles__")) return;
UNLESS(py__proxy_roles=PyString_FromString("_proxy_roles")) return;
UNLESS(py_hasRole=PyString_FromString("hasRole")) return;
UNLESS(py_validate=PyString_FromString("validate")) return;
UNLESS(py__push=PyString_FromString("_push")) return;
UNLESS(py__pop=PyString_FromString("_pop")) return;
UNLESS(py_Unauthorized=PyString_FromString("Unauthorized")) return;
UNLESS(py_Unauthorized_fmt=PyString_FromString(
"You are not authorized to access <em>%s</em>.")) return;
UNLESS(py_AUTHENTICATED_USER=PyString_FromString("AUTHENTICATED_USER"))
return;
UNLESS(py_=PyString_FromString("")) return;
UNLESS(join=PyImport_ImportModule("string")) return;
ASSIGN(join,PyObject_GetAttrString(join,"join"));
......@@ -561,6 +862,7 @@ initcDocumentTemplate()
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"cDocument",cDocumentType);
PyExtensionClass_Export(d,"InstanceDict",InstanceDictType);
PyExtensionClass_Export(d,"TemplateDict",MMtype);
......@@ -576,6 +878,28 @@ initcDocumentTemplate()
Revision Log:
$Log: cDocumentTemplate.c,v $
Revision 1.11 1998/04/02 17:37:40 jim
Major redesign of block rendering. The code inside a block tag is
compiled as a template but only the templates blocks are saved, and
later rendered directly with render_blocks.
Added with tag.
Also, for the HTML syntax, we now allow spaces after # and after end
or '/'. So, the tags::
<!--#
with spam
-->
and::
<!--#
end with
-->
are valid.
Revision 1.10 1998/03/26 21:55:40 jim
Fixed error propigation from aq_acquire in InstanceDict.
......
......@@ -58,12 +58,14 @@
__doc__='''Python implementations of document template some features
$Id: pDocumentTemplate.py,v 1.7 1997/11/19 15:42:48 jim Exp $'''
__version__='$Revision: 1.7 $'[11:-2]
$Id: pDocumentTemplate.py,v 1.8 1998/04/02 17:37:40 jim Exp $'''
__version__='$Revision: 1.8 $'[11:-2]
import regex, string
from string import join
StringType=type('')
TupleType=type(())
isFunctionType={}
for name in ['BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',
'FunctionType', 'LambdaType', 'MethodType']:
......@@ -86,6 +88,8 @@ class InstanceDict:
self.self=o
self.cache={}
self.namespace=namespace
if validate is None: self.validate=namespace.validate
else: self.validate=validate
def has_key(self,key):
return hasattr(self.self,key)
......@@ -128,7 +132,7 @@ class MultiMapping:
def push(self,d): self.dicts.insert(0,d)
def pop(self,n): del self.dicts[:n]
def pop(self,n=1): del self.dicts[:n]
def keys(self):
kz = []
......@@ -140,7 +144,7 @@ class TemplateDict:
level=0
def _pop(self, n): return self.dicts.pop(n)
def _pop(self, n=1): return self.dicts.pop(n)
def _push(self, d): return self.dicts.push(d)
def __init__(self):
......@@ -164,19 +168,86 @@ class TemplateDict:
getitem=__getitem__
def render_blocks(self, md):
def render_blocks(blocks, md):
rendered = []
for section in self.blocks:
if type(section) is not StringType:
append=rendered.append
for section in blocks:
if type(section) is TupleType:
l=len(section)
if l==1:
# Simple var
section=section[0]
if type(section) is StringType: 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 type(cond) is StringType:
n=cond
try:
cond=md[cond]
cache[n]=cond
except KeyError, v:
if n != v: raise KeyError, v, sys.exc_traceback
cond=None
else: cond=section(md)
if cond:
section=section[i+1]
if section: section=render_blocks(section,md)
else: section=''
m=0
break
i=i+2
if m:
if i==m: section=render_blocks(section[i],md)
else: section=''
finally: md._pop()
elif type(section) is not StringType:
section=section(md)
if section: rendered.append(section)
rendered=string.join(rendered, '')
l=len(rendered)
if l==0: return ''
elif l==1: return rendered[0]
return join(rendered, '')
return rendered
##############################################################################
#
# $Log: pDocumentTemplate.py,v $
# Revision 1.8 1998/04/02 17:37:40 jim
# Major redesign of block rendering. The code inside a block tag is
# compiled as a template but only the templates blocks are saved, and
# later rendered directly with render_blocks.
#
# Added with tag.
#
# Also, for the HTML syntax, we now allow spaces after # and after end
# or '/'. So, the tags::
#
# <!--#
# with spam
# -->
#
# and::
#
# <!--#
# end with
# -->
#
# are valid.
#
# Revision 1.7 1997/11/19 15:42:48 jim
# added _ prefix to push and pop methods to make them private
#
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment