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 ...@@ -6,8 +6,16 @@ __doc__='''Comments
The 'comment' tag can be used to simply include comments The 'comment' tag can be used to simply include comments
in DTML source. in DTML source.
''' # ' For example::
__rcs_id__='$Id: DT_Comment.py,v 1.1 1998/03/04 18:19:56 jim Exp $'
<!--#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 # Copyright
...@@ -61,7 +69,7 @@ __rcs_id__='$Id: DT_Comment.py,v 1.1 1998/03/04 18:19:56 jim Exp $' ...@@ -61,7 +69,7 @@ __rcs_id__='$Id: DT_Comment.py,v 1.1 1998/03/04 18:19:56 jim Exp $'
# (540) 371-6909 # (540) 371-6909
# #
############################################################################ ############################################################################
__version__='$Revision: 1.1 $'[11:-2] __version__='$Revision: 1.2 $'[11:-2]
from DT_Util import * from DT_Util import *
...@@ -81,6 +89,28 @@ class Comment: ...@@ -81,6 +89,28 @@ class Comment:
############################################################################ ############################################################################
# $Log: DT_Comment.py,v $ # $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 # Revision 1.1 1998/03/04 18:19:56 jim
# added comment and raise tags # added comment and raise tags
# #
......
...@@ -55,6 +55,8 @@ Two source formats are supported: ...@@ -55,6 +55,8 @@ Two source formats are supported:
is used to insert the variable 'total' with the C format is used to insert the variable 'total' with the C format
'12.2f'. '12.2f'.
%(Expr)s
%(Var)s %(Var)s
Document templates support conditional and sequence insertion Document templates support conditional and sequence insertion
...@@ -98,8 +100,6 @@ Access Control ...@@ -98,8 +100,6 @@ Access Control
object will have an attribute, AUTHENTICATED_USER that is the object will have an attribute, AUTHENTICATED_USER that is the
user object that was found if and when Bobo authenticated a user. user object that was found if and when Bobo authenticated a user.
%(Expr)s
Document Templates may be created 4 ways: Document Templates may be created 4 ways:
DocumentTemplate.String -- Creates a document templated from a DocumentTemplate.String -- Creates a document templated from a
......
"""HTML formated DocumentTemplates """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 from DT_String import String, FileMixin
import DT_Doc, DT_String, regex import DT_Doc, DT_String, regex
...@@ -12,20 +12,21 @@ from string import strip, find ...@@ -12,20 +12,21 @@ from string import strip, find
class dtml_re_class: class dtml_re_class:
def search(self, text, start=0, 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) s=find(text,'<!--#',start)
if s < 0: return s if s < 0: return s
e=find(text,'-->',s) e=find(text,'-->',s)
if e < 0: return e if e < 0: return e
n=s+5 n=s+5
if text[n:n+1]=='/': l=end_match(text,n)
end=text[n:n+1] if l > 0:
n=n+1 end=strip(text[n:n+l])
elif text[n:n+3]=='end': n=n+l
end=text[n:n+3] else: end=''
n=n+3
else:
end=''
l=name_match(text,n) l=name_match(text,n)
if l < 0: return l if l < 0: return l
...@@ -59,14 +60,6 @@ class HTML(DT_String.String): ...@@ -59,14 +60,6 @@ class HTML(DT_String.String):
def tagre(self): def tagre(self):
return dtml_re_class() 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=''): def parseTag(self, tagre, command=None, sargs=''):
"""Parse a tag using an already matched re """Parse a tag using an already matched re
...@@ -218,6 +211,28 @@ class HTMLFile(FileMixin, HTML): ...@@ -218,6 +211,28 @@ class HTMLFile(FileMixin, HTML):
########################################################################## ##########################################################################
# #
# $Log: DT_HTML.py,v $ # $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 # Revision 1.5 1997/10/27 17:35:32 jim
# Removed old validation machinery. # Removed old validation machinery.
# #
......
...@@ -117,8 +117,8 @@ __doc__='''Conditional insertion ...@@ -117,8 +117,8 @@ __doc__='''Conditional insertion
# (540) 371-6909 # (540) 371-6909
# #
############################################################################ ############################################################################
__rcs_id__='$Id: DT_If.py,v 1.8 1998/01/14 18:23:42 jim Exp $' __rcs_id__='$Id: DT_If.py,v 1.9 1998/04/02 17:37:35 jim Exp $'
__version__='$Revision: 1.8 $'[11:-2] __version__='$Revision: 1.9 $'[11:-2]
from DT_Util import * from DT_Util import *
import sys import sys
...@@ -135,17 +135,20 @@ class If: ...@@ -135,17 +135,20 @@ class If:
args=parse_params(args, name='', expr='') args=parse_params(args, name='', expr='')
name,expr=name_param(args,'if',1) name,expr=name_param(args,'if',1)
self.__name__= name 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': if blocks[-1][0]=='else':
tname, args, section = blocks[-1] tname, args, section = blocks[-1]
blocks=blocks[:-1] del blocks[-1]
args=parse_params(args, name='') args=parse_params(args, name='')
if args: if args:
ename,expr=name_param(args,'else',1) ename,expr=name_param(args,'else',1)
if ename != name: if ename != name:
raise ParseError, ('name in else does not match if', 'in') 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:]: for tname, args, section in blocks[1:]:
if tname=='else': if tname=='else':
...@@ -153,32 +156,14 @@ class If: ...@@ -153,32 +156,14 @@ class If:
'more than one else tag for a single if tag', 'in') 'more than one else tag for a single if tag', 'in')
args=parse_params(args, name='', expr='') args=parse_params(args, name='', expr='')
name,expr=name_param(args,'elif',1) name,expr=name_param(args,'elif',1)
self.sections.append((name, expr, section)) if expr is None: cond=name
else: cond=expr.eval
def render(self,md): sections.append(cond)
cache={} sections.append(section.blocks)
md._push(cache)
try: if elses is not None: sections.append(elses)
for name, expr, section in self.sections:
if expr is None: self.simple_form=tuple(sections)
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
class Unless: class Unless:
name='unless' name='unless'
...@@ -188,28 +173,9 @@ class Unless: ...@@ -188,28 +173,9 @@ class Unless:
tname, args, section = blocks[0] tname, args, section = blocks[0]
args=parse_params(args, name='', expr='') args=parse_params(args, name='', expr='')
name,expr=name_param(args,'unless',1) name,expr=name_param(args,'unless',1)
self.__name__ = name if expr is None: cond=name
self.section=section else: cond=expr.eval
self.expr=expr self.simple_form=(cond,None,section.blocks)
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
class Else(Unless): class Else(Unless):
# The else tag is included for backward compatibility and is deprecated. # The else tag is included for backward compatibility and is deprecated.
...@@ -219,6 +185,28 @@ class Else(Unless): ...@@ -219,6 +185,28 @@ class Else(Unless):
########################################################################## ##########################################################################
# #
# $Log: DT_If.py,v $ # $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 # Revision 1.8 1998/01/14 18:23:42 jim
# Added expr to unless. # Added expr to unless.
# #
......
...@@ -296,7 +296,7 @@ ...@@ -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 # Copyright
...@@ -350,18 +350,28 @@ __rcs_id__='$Id: DT_In.py,v 1.22 1998/03/20 17:52:29 jim Exp $' ...@@ -350,18 +350,28 @@ __rcs_id__='$Id: DT_In.py,v 1.22 1998/03/20 17:52:29 jim Exp $'
# (540) 371-6909 # (540) 371-6909
# #
############################################################################ ############################################################################
__version__='$Revision: 1.22 $'[11:-2] __version__='$Revision: 1.23 $'[11:-2]
from DT_Util import * from DT_Util import *
from string import find, atoi, join from string import find, atoi, join
import regex import regex
from regsub import gsub from regsub import gsub
from DT_InSV import sequence_variables, opt
class In: class InFactory:
blockContinuations=('else',) blockContinuations=('else',)
name='in' 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 elses=None
expr=None expr=sort=batch=mapping=None
start_name_re=None start_name_re=None
def __init__(self, blocks): def __init__(self, blocks):
...@@ -370,7 +380,23 @@ class In: ...@@ -370,7 +380,23 @@ class In:
orphan='3',overlap='1',mapping=1, orphan='3',overlap='1',mapping=1,
previous=1, next=1, expr='', sort='') previous=1, next=1, expr='', sort='')
self.args=args 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'] v=args['start']
if type(v)==type(''): if type(v)==type(''):
try: atoi(v) try: atoi(v)
...@@ -381,8 +407,9 @@ class In: ...@@ -381,8 +407,9 @@ class In:
'=[0-9]+&+') '=[0-9]+&+')
name,expr=name_param(args,'in',1) name,expr=name_param(args,'in',1)
if expr is not None: expr=expr.eval
self.__name__, self.expr = name, expr self.__name__, self.expr = name, expr
self.section=section self.section=section.blocks
if len(blocks) > 1: if len(blocks) > 1:
if len(blocks) != 2: raise ParseError, ( if len(blocks) != 2: raise ParseError, (
'too many else blocks', 'in') 'too many else blocks', 'in')
...@@ -393,58 +420,40 @@ class In: ...@@ -393,58 +420,40 @@ class In:
if ename != name: if ename != name:
raise ParseError, ( raise ParseError, (
'name in else does not match in', 'in') '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 expr=self.expr
if expr is None: sequence=md[self.__name__] name=self.__name__
else: sequence=expr.eval(md) if expr is None:
sequence=md[name]
cache={ name: sequence }
else:
sequence=expr(md)
cache=None
if not sequence: if not sequence:
if self.elses: return self.elses(None, md) if self.elses: return render_blocks(self.elses, md)
return '' return ''
section=self.section section=self.section
params=self.args params=self.args
nbatchparams=len(params)-1
mapping=self.mapping
if params.has_key('mapping'): if self.sort is not None: sequence=self.sort_sequence(sequence)
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)
next=previous=0 next=previous=0
if nbatchparams: try: start=int_param(params,md,'start',0)
try: start=int_param(params,md,'start',0) except: start=1
except: start=1 end=int_param(params,md,'end',0)
end=int_param(params,md,'end',0) size=int_param(params,md,'size',0)
size=int_param(params,md,'size',0) overlap=int_param(params,md,'overlap',0)
overlap=int_param(params,md,'overlap',0) orphan=int_param(params,md,'orphan','3')
orphan=int_param(params,md,'orphan','3') start,end,sz=opt(start,end,size,orphan,sequence)
start,end,sz=opt(start,end,size,orphan,sequence) if params.has_key('next'): next=1
if params.has_key('next'): next=1 if params.has_key('previous'): previous=1
if params.has_key('previous'): previous=1
else:
start=1
end=len(sequence)
last=end-1 last=end-1
first=start-1 first=start-1
...@@ -454,18 +463,22 @@ class In: ...@@ -454,18 +463,22 @@ class In:
vars=sequence_variables(sequence,'?'+query_string,self.start_name_re) vars=sequence_variables(sequence,'?'+query_string,self.start_name_re)
kw=vars.data kw=vars.data
if expr is None: kw[self.__name__]=sequence # Cache sequence
kw['mapping']=mapping kw['mapping']=mapping
if nbatchparams: kw['sequence-step-size']=sz
kw['sequence-step-size']=sz kw['sequence-step-overlap']=overlap
kw['sequence-step-overlap']=overlap kw['sequence-step-start']=start
kw['sequence-step-start']=start kw['sequence-step-end']=end
kw['sequence-step-end']=end kw['sequence-step-start-index']=start-1
kw['sequence-step-start-index']=start-1 kw['sequence-step-end-index']=end-1
kw['sequence-step-end-index']=end-1 kw['sequence-step-orphan']=orphan
kw['sequence-step-orphan']=orphan
push=md._push
pop=md._pop
render=render_blocks
if cache: push(cache)
push(vars)
try: try:
md._push(vars)
if previous: if previous:
if first > 0: if first > 0:
pstart,pend,psize=opt(None,first+overlap, pstart,pend,psize=opt(None,first+overlap,
...@@ -474,13 +487,13 @@ class In: ...@@ -474,13 +487,13 @@ class In:
kw['previous-sequence-start-index']=pstart-1 kw['previous-sequence-start-index']=pstart-1
kw['previous-sequence-end-index']=pend-1 kw['previous-sequence-end-index']=pend-1
kw['previous-sequence-size']=pend+1-pstart 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='' else: result=''
elif next: elif next:
try: 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 # there are more items, without actually
# computing a length: # computing a length:
sequence[end] sequence[end]
...@@ -490,60 +503,154 @@ class In: ...@@ -490,60 +503,154 @@ class In:
kw['next-sequence-start-index']=pstart-1 kw['next-sequence-start-index']=pstart-1
kw['next-sequence-end-index']=pend-1 kw['next-sequence-end-index']=pend-1
kw['next-sequence-size']=pend+1-pstart kw['next-sequence-size']=pend+1-pstart
result=section(None,md) result=render(section,md)
except: except:
if self.elses: result=self.elses(None, md) if self.elses: result=render(self.elses, md)
else: result='' else: result=''
else: else:
result = [] result = []
for index in range(first,end): append=result.append
if nbatchparams: for index in range(first,end):
if index==first and index > 0: if index==first and index > 0:
pstart,pend,psize=opt(None,index+overlap, pstart,pend,psize=opt(None,index+overlap,
sz,orphan,sequence) sz,orphan,sequence)
kw['previous-sequence']=1 kw['previous-sequence']=1
kw['previous-sequence-start-index']=pstart-1 kw['previous-sequence-start-index']=pstart-1
kw['previous-sequence-end-index']=pend-1 kw['previous-sequence-end-index']=pend-1
kw['previous-sequence-size']=pend+1-pstart kw['previous-sequence-size']=pend+1-pstart
else: else:
kw['previous-sequence']=0 kw['previous-sequence']=0
if index==last: if index==last:
try: try:
# The following line is a neaky way to # The following line is a sneaky way to
# test whether there are more items, # test whether there are more items,
# without actually computing a length: # without actually computing a length:
sequence[end] sequence[end]
pstart,pend,psize=opt(end+1-overlap,None, pstart,pend,psize=opt(end+1-overlap,None,
sz,orphan,sequence) sz,orphan,sequence)
kw['previous-sequence']=0 kw['previous-sequence']=0
kw['next-sequence']=1 kw['next-sequence']=1
kw['next-sequence-start-index']=pstart-1 kw['next-sequence-start-index']=pstart-1
kw['next-sequence-end-index']=pend-1 kw['next-sequence-end-index']=pend-1
kw['next-sequence-size']=pend+1-pstart kw['next-sequence-size']=pend+1-pstart
except: pass except: pass
if index==first: kw['sequence-start']=1
else: kw['sequence-start']=0
if index==last: kw['sequence-end']=1 if index==last: kw['sequence-end']=1
else: kw['sequence-end']=0
client=sequence[index] client=sequence[index]
kw['sequence-index']=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: if type(client)==TupleType and len(client)==2:
client=client[1] client=client[1]
if mapping:
client=mapping_wrapper(client) if mapping: push(client)
result.append(section(client,md)) else: push(InstanceDict(client, md))
try: append(render(section, md))
finally: pop(1)
if index==first: kw['sequence-start']=0
result=join(result, '') result=join(result, '')
finally: finally:
md._pop(1) if cache: pop()
pop()
return result 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): def int_param(params,md,name,default=0):
try: v=params[name] try: v=params[name]
...@@ -556,324 +663,30 @@ def int_param(params,md,name,default=0): ...@@ -556,324 +663,30 @@ def int_param(params,md,name,default=0):
v=atoi(v) v=atoi(v)
return 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 $ # $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 # Revision 1.22 1998/03/20 17:52:29 jim
# Added new meaning for else tag when next or previous are used. # 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 * 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 Var=DT_Var.Var
from DT_Util import * from DT_Util import *
...@@ -28,6 +28,7 @@ class String: ...@@ -28,6 +28,7 @@ class String:
'var': DT_Var.Var, 'var': DT_Var.Var,
'call': DT_Var.Call, 'call': DT_Var.Call,
'in': DT_In.In, 'in': DT_In.In,
'with': DT_With.With,
'if': DT_If.If, 'if': DT_If.If,
'unless': DT_If.Unless, 'unless': DT_If.Unless,
'else': DT_If.Else, 'else': DT_If.Else,
...@@ -108,10 +109,10 @@ class String: ...@@ -108,10 +109,10 @@ class String:
tag, l, args, command) tag, l, args, command)
else: else:
try: try:
if command is Var: if command is Var: r=command(args, self.varExtra(tagre))
result.append(command(args, self.varExtra(tagre))) else: r=command(args)
else: if hasattr(r,'simple_form'): r=r.simple_form
result.append(command(args)) result.append(r)
except ParseError, m: self.parse_error(m[0],tag,text,l) except ParseError, m: self.parse_error(m[0],tag,text,l)
l=tagre.search(text,start) l=tagre.search(text,start)
...@@ -166,8 +167,9 @@ class String: ...@@ -166,8 +167,9 @@ class String:
sstart=start sstart=start
else: else:
try: try:
if scommand is not Comment: r=scommand(blocks)
result.append(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) except ParseError, m: self.parse_error(m[0],stag,text,l)
return start return start
...@@ -365,7 +367,7 @@ class String: ...@@ -365,7 +367,7 @@ class String:
md.level=level+1 md.level=level+1
if client is not None: if client is not None:
push(InstanceDict(client,md,self.validate)) # Circ. Ref. 8-| push(InstanceDict(client,md)) # Circ. Ref. 8-|
pushed=pushed+1 pushed=pushed+1
if self._vars: if self._vars:
...@@ -377,7 +379,7 @@ class String: ...@@ -377,7 +379,7 @@ class String:
pushed=pushed+1 pushed=pushed+1
try: try:
return render_blocks(self,md) return render_blocks(self.blocks,md)
finally: finally:
if pushed: md._pop(pushed) # Get rid of circular reference! if pushed: md._pop(pushed) # Get rid of circular reference!
md.level=level # Restore previous level 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 # Copyright
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
# (540) 371-6909 # (540) 371-6909
# #
############################################################################ ############################################################################
__version__='$Revision: 1.30 $'[11:-2] __version__='$Revision: 1.31 $'[11:-2]
import sys, regex, string, types, math, os import sys, regex, string, types, math, os
from string import rfind, strip, joinfields, atoi,lower,upper,capitalize from string import rfind, strip, joinfields, atoi,lower,upper,capitalize
...@@ -327,8 +327,32 @@ def parse_params(text, ...@@ -327,8 +327,32 @@ def parse_params(text,
try: from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks try: from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks
except: from pDocumentTemplate 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 $ # $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 # Revision 1.30 1998/03/26 22:02:16 jim
# Changed value of ValidationError to Unauthorized. # Changed value of ValidationError to Unauthorized.
# #
......
...@@ -105,8 +105,16 @@ __doc__='''Variable insertion parameters ...@@ -105,8 +105,16 @@ __doc__='''Variable insertion parameters
is used. For example, if the value of spam is is used. For example, if the value of spam is
'"blah blah blah blah"', then the tag '"blah blah blah blah"', then the tag
'<!--#var spam size=10-->' inserts '"blah blah ..."'. '<!--#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 # Copyright
...@@ -160,7 +168,7 @@ __rcs_id__='$Id: DT_Var.py,v 1.11 1998/03/24 20:21:39 jim Exp $' ...@@ -160,7 +168,7 @@ __rcs_id__='$Id: DT_Var.py,v 1.11 1998/03/24 20:21:39 jim Exp $'
# (540) 371-6909 # (540) 371-6909
# #
############################################################################ ############################################################################
__version__='$Revision: 1.11 $'[11:-2] __version__='$Revision: 1.12 $'[11:-2]
from DT_Util import * from DT_Util import *
...@@ -189,6 +197,11 @@ class Var: ...@@ -189,6 +197,11 @@ class Var:
self.__name__, self.expr = name, expr self.__name__, self.expr = name, expr
self.fmt = fmt 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): def render(self, md):
name=self.__name__ name=self.__name__
val=self.expr val=self.expr
...@@ -259,16 +272,10 @@ class Call: ...@@ -259,16 +272,10 @@ class Call:
def __init__(self, args): def __init__(self, args):
args = parse_params(args, name='', expr='') 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={}, def html_quote(v, name='(Unknown name)', md={},
character_entities=( character_entities=(
...@@ -360,6 +367,28 @@ modifiers=map(lambda f: (f.__name__, f), modifiers) ...@@ -360,6 +367,28 @@ modifiers=map(lambda f: (f.__name__, f), modifiers)
############################################################################ ############################################################################
# $Log: DT_Var.py,v $ # $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 # Revision 1.11 1998/03/24 20:21:39 jim
# Added 'call' tag. # 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 @@ ...@@ -2,7 +2,7 @@
"""Document Template Tests """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 # Copyright
...@@ -56,7 +56,7 @@ __rcs_id__='$Id: DTtest.py,v 1.3 1997/11/11 18:39:00 jim Exp $' ...@@ -56,7 +56,7 @@ __rcs_id__='$Id: DTtest.py,v 1.3 1997/11/11 18:39:00 jim Exp $'
# (540) 371-6909 # (540) 371-6909
# #
############################################################################ ############################################################################
__version__='$Revision: 1.3 $'[11:-2] __version__='$Revision: 1.4 $'[11:-2]
from DocumentTemplate import * from DocumentTemplate import *
import sys import sys
...@@ -121,7 +121,7 @@ def test1(): ...@@ -121,7 +121,7 @@ def test1():
print ss(aa) print ss(aa)
print 'num inaccessible:' print 'num inaccessible:'
ss.names({'args':'args'}) # ss.names({'args':'args'})
print ss(aa) print ss(aa)
print 'quoted source:' print 'quoted source:'
...@@ -408,11 +408,11 @@ def test8(): ...@@ -408,11 +408,11 @@ def test8():
def test9(): def test9():
html=HTML( html=HTML(
""" """
<!--#in spam--> <!--#in spam-->
<!--#in sequence-item--> <!--#in sequence-item-->
<!--#var sequence-item--> <!--#var sequence-item-->
<!--#/in sequence-item--> <!--#/in sequence-item-->
<!--#/in spam--> <!--#/in spam-->
""") """)
print html(spam=[[1,2,3],[4,5,6]]) print html(spam=[[1,2,3],[4,5,6]])
...@@ -506,12 +506,6 @@ def main(): ...@@ -506,12 +506,6 @@ def main():
print 'Test 6', '='*60 print 'Test 6', '='*60
try: test6() try: test6()
except: traceback.print_exc() 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 print 'Test 9', '='*60
try: test9() try: test9()
except: traceback.print_exc() except: traceback.print_exc()
...@@ -540,6 +534,28 @@ if __name__ == "__main__": ...@@ -540,6 +534,28 @@ if __name__ == "__main__":
############################################################################ ############################################################################
# $Log: DTtest.py,v $ # $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 # Revision 1.3 1997/11/11 18:39:00 jim
# Added test for: # Added test for:
# Made sequence-items work when iterating over mapping items. # Made sequence-items work when iterating over mapping items.
......
#!/usr/local/bin/python #!/usr/local/bin/python
# $What$ # $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__ % { __doc__=DT_Doc.__doc__ % {
'In': DT_In.__doc__, 'In': DT_In.__doc__,
'If': DT_If.__doc__, 'If': DT_If.__doc__,
'Var': DT_Var.__doc__, 'Var': DT_Var.__doc__,
'Expr': DT_Util.Expr_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__ % { ...@@ -62,7 +65,7 @@ __doc__=DT_Doc.__doc__ % {
# (540) 371-6909 # (540) 371-6909
# #
############################################################################ ############################################################################
__version__='$Revision: 1.4 $'[11:-2] __version__='$Revision: 1.5 $'[11:-2]
ParseError='Document Template Parse Error' ParseError='Document Template Parse Error'
...@@ -73,6 +76,28 @@ from DT_Var import html_quote ...@@ -73,6 +76,28 @@ from DT_Var import html_quote
############################################################################ ############################################################################
# $Log: DocumentTemplate.py,v $ # $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 # Revision 1.4 1997/10/29 22:06:32 jim
# Cleaned up imports. # Cleaned up imports.
# #
......
"""Very Safe Python Expressions """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 # Copyright
...@@ -11,7 +11,7 @@ __rcs_id__='$Id: VSEval.py,v 1.11 1998/03/12 21:37:01 jim Exp $' ...@@ -11,7 +11,7 @@ __rcs_id__='$Id: VSEval.py,v 1.11 1998/03/12 21:37:01 jim Exp $'
# rights reserved. # rights reserved.
# #
############################################################################ ############################################################################
__version__='$Revision: 1.11 $'[11:-2] __version__='$Revision: 1.12 $'[11:-2]
from string import join, find, split, translate from string import join, find, split, translate
import sys, gparse, string import sys, gparse, string
...@@ -73,6 +73,7 @@ class Eval: ...@@ -73,6 +73,7 @@ class Eval:
globals -- A global namespace. globals -- A global namespace.
""" """
self.__name__=expr
expr=translate(expr,nltosp) expr=translate(expr,nltosp)
self.expr=expr self.expr=expr
self.globals=globals self.globals=globals
...@@ -131,6 +132,28 @@ compiled_getattr=compile( ...@@ -131,6 +132,28 @@ compiled_getattr=compile(
############################################################################ ############################################################################
# #
# $Log: VSEval.py,v $ # $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 # Revision 1.11 1998/03/12 21:37:01 jim
# Added _getattr. # Added _getattr.
# #
......
/*********************************************************** /******************************************************************
Copyright Copyright
Copyright 1997 Digital Creations, L.L.C., 910 Princess Anne Copyright 1997 Digital Creations, L.L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved. rights reserved.
******************************************************************/ ******************************************************************/
static char cDocumentTemplate_module_documentation[] = 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" #include "ExtensionClass.h"
static PyObject *py_isDocTemp=0, *py_blocks=0, *py_=0, *join=0, *py_acquire; 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 { ...@@ -36,16 +39,22 @@ typedef struct {
staticforward PyExtensionClass InstanceDictType; staticforward PyExtensionClass InstanceDictType;
static PyObject * 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->inst),
&(self->namespace), &(self->namespace),
&(self->validate))) &(self->validate)))
return NULL; return NULL;
Py_INCREF(self->inst); Py_INCREF(self->inst);
Py_INCREF(self->namespace); 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; UNLESS(self->cache=PyDict_New()) return NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
...@@ -291,7 +300,8 @@ MM_cget(MM *self, PyObject *key, int call) ...@@ -291,7 +300,8 @@ MM_cget(MM *self, PyObject *key, int call)
if(dt) if(dt)
{ {
ASSIGN(e,PyObject_CallFunction(e,"OO", Py_None, self)); ASSIGN(e,PyObject_CallFunction(e,"OO", Py_None, self));
UNLESS(e) return NULL; UNLESS(e)
return NULL;
} }
else else
{ {
...@@ -493,62 +503,353 @@ static struct PyMethodDef TemplateDict_methods[] = { ...@@ -493,62 +503,353 @@ static struct PyMethodDef TemplateDict_methods[] = {
/* List of methods defined in the module */ /* List of methods defined in the module */
static PyObject * static int
render_blocks(PyObject *self, PyObject *args) if_finally(PyObject *md, int err)
{ {
PyObject *md, *blocks, *rendered, *block; PyObject *t, *v, *tb;
int l, i, k;
UNLESS(PyArg_ParseTuple(args,"OO", &self, &md)) return NULL; if(err) PyErr_Fetch(&t, &v, &tb);
UNLESS(md=Py_BuildValue("(O)",md)) return NULL;
UNLESS(rendered=PyList_New(0)) goto err; md=PyObject_GetAttr(md,py__pop);
UNLESS(blocks=PyObject_GetAttr(self,py_blocks)) goto err; if(md) ASSIGN(md, PyObject_CallObject(md,NULL));
if((l=PyList_Size(blocks)) < 0) goto err;
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++) for(i=0; i < l; i++)
{ {
block=PyList_GET_ITEM(((PyListObject*)blocks), 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 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); k=PyList_Append(rendered,block);
Py_DECREF(block); Py_DECREF(block);
if(k < 0) goto err; if(k < 0) return -1;
} }
} }
Py_DECREF(md);
Py_DECREF(blocks); return 0;
ASSIGN(rendered,PyObject_CallFunction(join,"OO",rendered,py_)); }
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; return rendered;
err: err:
Py_DECREF(md); Py_XDECREF(mda);
Py_XDECREF(rendered); Py_XDECREF(rendered);
Py_XDECREF(blocks);
return NULL; return NULL;
} }
static struct PyMethodDef Module_Level__methods[] = { static struct PyMethodDef Module_Level__methods[] = {
{"render_blocks", (PyCFunction)render_blocks, METH_VARARGS, {"render_blocks", (PyCFunction)render_blocks, METH_VARARGS,
""}, ""},
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ {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) */ /* Initialization function for the module (*must* be called initcDocumentTemplate) */
void void
initcDocumentTemplate() initcDocumentTemplate()
{ {
PyObject *m, *d; 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_isDocTemp=PyString_FromString("isDocTemp")) return;
UNLESS(py_blocks=PyString_FromString("blocks")) return; UNLESS(py_blocks=PyString_FromString("blocks")) return;
UNLESS(py_acquire=PyString_FromString("aq_acquire")) return; UNLESS(py_acquire=PyString_FromString("aq_acquire")) return;
UNLESS(py___call__=PyString_FromString("__call__")) 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(py_=PyString_FromString("")) return;
UNLESS(join=PyImport_ImportModule("string")) return; UNLESS(join=PyImport_ImportModule("string")) return;
ASSIGN(join,PyObject_GetAttrString(join,"join")); ASSIGN(join,PyObject_GetAttrString(join,"join"));
...@@ -561,6 +862,7 @@ initcDocumentTemplate() ...@@ -561,6 +862,7 @@ initcDocumentTemplate()
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"cDocument",cDocumentType);
PyExtensionClass_Export(d,"InstanceDict",InstanceDictType); PyExtensionClass_Export(d,"InstanceDict",InstanceDictType);
PyExtensionClass_Export(d,"TemplateDict",MMtype); PyExtensionClass_Export(d,"TemplateDict",MMtype);
...@@ -576,6 +878,28 @@ initcDocumentTemplate() ...@@ -576,6 +878,28 @@ initcDocumentTemplate()
Revision Log: Revision Log:
$Log: cDocumentTemplate.c,v $ $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 Revision 1.10 1998/03/26 21:55:40 jim
Fixed error propigation from aq_acquire in InstanceDict. Fixed error propigation from aq_acquire in InstanceDict.
......
...@@ -58,12 +58,14 @@ ...@@ -58,12 +58,14 @@
__doc__='''Python implementations of document template some features __doc__='''Python implementations of document template some features
$Id: pDocumentTemplate.py,v 1.7 1997/11/19 15:42:48 jim Exp $''' $Id: pDocumentTemplate.py,v 1.8 1998/04/02 17:37:40 jim Exp $'''
__version__='$Revision: 1.7 $'[11:-2] __version__='$Revision: 1.8 $'[11:-2]
import regex, string import regex, string
from string import join
StringType=type('') StringType=type('')
TupleType=type(())
isFunctionType={} isFunctionType={}
for name in ['BuiltinFunctionType', 'BuiltinMethodType', 'ClassType', for name in ['BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',
'FunctionType', 'LambdaType', 'MethodType']: 'FunctionType', 'LambdaType', 'MethodType']:
...@@ -86,6 +88,8 @@ class InstanceDict: ...@@ -86,6 +88,8 @@ class InstanceDict:
self.self=o self.self=o
self.cache={} self.cache={}
self.namespace=namespace self.namespace=namespace
if validate is None: self.validate=namespace.validate
else: self.validate=validate
def has_key(self,key): def has_key(self,key):
return hasattr(self.self,key) return hasattr(self.self,key)
...@@ -128,7 +132,7 @@ class MultiMapping: ...@@ -128,7 +132,7 @@ class MultiMapping:
def push(self,d): self.dicts.insert(0,d) 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): def keys(self):
kz = [] kz = []
...@@ -140,7 +144,7 @@ class TemplateDict: ...@@ -140,7 +144,7 @@ class TemplateDict:
level=0 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 _push(self, d): return self.dicts.push(d)
def __init__(self): def __init__(self):
...@@ -164,19 +168,86 @@ class TemplateDict: ...@@ -164,19 +168,86 @@ class TemplateDict:
getitem=__getitem__ getitem=__getitem__
def render_blocks(self, md): def render_blocks(blocks, md):
rendered = [] rendered = []
for section in self.blocks: append=rendered.append
if type(section) is not StringType: 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) section=section(md)
if section: rendered.append(section) 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 return rendered
############################################################################## ##############################################################################
# #
# $Log: pDocumentTemplate.py,v $ # $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 # Revision 1.7 1997/11/19 15:42:48 jim
# added _ prefix to push and pop methods to make them private # 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