405 lines
10 KiB
Python
405 lines
10 KiB
Python
import io
|
|
import re
|
|
import sys
|
|
import os
|
|
|
|
import hserver.api
|
|
|
|
import html
|
|
|
|
import builtins
|
|
|
|
from simplelog import log
|
|
|
|
|
|
def text2html(text):
|
|
lines = text.splitlines()
|
|
html = ""
|
|
for l in lines:
|
|
html = "{0}<p>{1}</p>".format(html,l)
|
|
return html
|
|
|
|
class SandboxPrinter:
|
|
|
|
def __init__(self,stream):
|
|
self.stream = stream
|
|
|
|
def __call__(self,te = ""):
|
|
self.stream.write(str(te))
|
|
|
|
class SandboxInclude:
|
|
|
|
def __init__(self,template):
|
|
self.__template = template
|
|
|
|
def __call__(self,template,selfref = None):
|
|
if (selfref is None):
|
|
self.__template.include( template )
|
|
else:
|
|
self.__template.include( [template, selfref] )
|
|
|
|
class TemplateSegment:
|
|
|
|
def __init__(self,template,parent = None):
|
|
self.__template = template
|
|
self.__parent = parent
|
|
|
|
def setParent(self,parent):
|
|
self.__parent = parent
|
|
|
|
def getParent(self):
|
|
return self.__parent
|
|
|
|
def getTemplate(self):
|
|
return self.__template
|
|
|
|
def run(self,env,priv = {}):
|
|
"""Run this Segement of the Template"""
|
|
pass
|
|
|
|
def __repr__(self):
|
|
return "<TemplateSegment>"
|
|
|
|
|
|
class TemplateGroupSegment(TemplateSegment):
|
|
|
|
def __init__(self,template,parent = None):
|
|
TemplateSegment.__init__(self,template,parent)
|
|
self.__children = []
|
|
|
|
def appendChild(self,segment):
|
|
self.__children.append(segment)
|
|
|
|
def getChildren(self):
|
|
return self.__children
|
|
|
|
def run(self,env,priv={}):
|
|
for child in self.__children:
|
|
child.run(env,priv)
|
|
|
|
def __repr__(self):
|
|
return "<TemplateGroupSegment( %s )>" % (str(self.getChildren()),)
|
|
|
|
class TemplateTextSegment(TemplateSegment):
|
|
|
|
def __init__(self,template,parent = None,text = ""):
|
|
TemplateSegment.__init__(self,template,parent)
|
|
self.__text = text
|
|
|
|
def run(self,env,priv={}):
|
|
self.getTemplate().getContentFile().write(self.__text)
|
|
|
|
def __repr__(self):
|
|
return "<TemplateTextSegment>"
|
|
|
|
class TemplateCodeSegment(TemplateSegment):
|
|
def __init__(self,template,parent = None,code = ""):
|
|
TemplateSegment.__init__(self,template,parent)
|
|
self.__code = code
|
|
|
|
def run(self,env,priv={}):
|
|
exec( self.__code, env )
|
|
|
|
def __repr__(self):
|
|
return "<TemplateCodeSegment(%s)>" %(self.__code,)
|
|
|
|
|
|
class TemplateValueSegment(TemplateSegment):
|
|
def __init__(self,template,parent = None,code = ""):
|
|
TemplateSegment.__init__(self,template,parent)
|
|
self.__code = code
|
|
|
|
def run(self,env,priv={}):
|
|
code = self.__code
|
|
v = eval( code, env )
|
|
self.getTemplate().getContentFile().write(str(v))
|
|
|
|
class TemplateIterateSegment(TemplateGroupSegment):
|
|
def __init__(self,template,parent = None,code = ""):
|
|
TemplateGroupSegment.__init__(self,template,parent)
|
|
(self.__iterator, self.__iteration) = code.split(" ",1)
|
|
|
|
def run(self,env,priv={}):
|
|
iter = eval( self.__iteration, env )
|
|
for v in iter:
|
|
env[self.__iterator] = v
|
|
TemplateGroupSegment.run(self,env,priv)
|
|
|
|
def __repr__(self):
|
|
return "<TemplateIterateSegment( %s=%s : %s )>" % (self.__iterator,self.__iteration,self.getChildren(),)
|
|
|
|
class TemplateIfSegment(TemplateGroupSegment):
|
|
def __init__(self,template,parent = None,code = ""):
|
|
TemplateGroupSegment.__init__(self,template,parent)
|
|
self.__code = code
|
|
|
|
def run(self,env,priv={}):
|
|
v = eval( self.__code, env )
|
|
if (v):
|
|
TemplateGroupSegment.run(self,env,priv)
|
|
|
|
def __repr__(self):
|
|
return "<TemplateIfSegment( %s )>" % (str(self.__children),)
|
|
|
|
|
|
class TemplateIncludeSegment(TemplateSegment):
|
|
def __init__(self,template,parent = None,code = ""):
|
|
TemplateSegment.__init__(self,template,parent)
|
|
self.__code = code
|
|
|
|
def run(self,env,priv={}):
|
|
v = eval( self.__code, env )
|
|
self.getTemplate().include(v,priv={})
|
|
|
|
def __repr__(self):
|
|
return "<TemplateIncludeSegment(%s)>" % (str(self.__code),)
|
|
|
|
class TemplateContentSegment(TemplateSegment):
|
|
def __init__(self,template,parent = None):
|
|
TemplateSegment.__init__(self,template,parent)
|
|
|
|
def run(self,env,priv={}):
|
|
if (not priv is None) and ("framed" in priv) and (not priv["framed"] is None):
|
|
priv["framed"].framed(env,priv)
|
|
|
|
def __repr__(self):
|
|
return "<TemplateContentSegment(%s)>" % (str(self.__code),)
|
|
|
|
|
|
class Template(hserver.api.WebCallable):
|
|
|
|
def __init__(self,template=None,base = ".",source = None,disable_sandbox=False,frame=None,provider=None):
|
|
self.template = template
|
|
self.base = base
|
|
self.__content = None
|
|
self.__frame = frame
|
|
self.__source = source
|
|
self.__root = None
|
|
self.__current = self.__root
|
|
self.__disable_sandbox = disable_sandbox
|
|
self.__timestamp = None
|
|
self.__provider = provider
|
|
|
|
def getContent(self):
|
|
return self.__content.getvalue()
|
|
|
|
def getContentFile(self):
|
|
return self.__content
|
|
|
|
def setFrame(self,frame):
|
|
self.__frame = frame
|
|
|
|
def provider(self):
|
|
return self.__provider
|
|
|
|
def __appendSegment(self,segment):
|
|
self.__current.appendChild(segment)
|
|
|
|
def __appendGroup(self,segment):
|
|
self.__appendSegment(segment)
|
|
self.__current = segment
|
|
|
|
def __leaveGroup(self):
|
|
if (self.__current != self.__root) and (self.__current.getParent() is not None):
|
|
self.__current = self.__current.getParent()
|
|
|
|
def indexWS(self,te):
|
|
p = 0
|
|
while (p < len(te)):
|
|
if (ord(te[p]) <= 0x20):
|
|
return p
|
|
p = p + 1
|
|
|
|
return p
|
|
|
|
|
|
def __splittag(self,tag):
|
|
"Split <tag> to a list of [ <action>, <expr> ]"
|
|
if (ord(tag[0]) <= 0x20):
|
|
return ["",tag[1:]]
|
|
elif (tag[0]=="="):
|
|
return ["=",tag[1:]]
|
|
else:
|
|
i = self.indexWS(tag)
|
|
if (i == len(tag)):
|
|
return [ tag , "" ]
|
|
else:
|
|
return [ tag[:i], tag[ i+1: ] ]
|
|
|
|
def __parsetag(self,tag):
|
|
(action,expr) = self.__splittag(tag)
|
|
|
|
if (action == ""):
|
|
self.__appendSegment( TemplateCodeSegment(self,self.__current,expr) )
|
|
elif (action == "="):
|
|
self.__appendSegment( TemplateValueSegment(self,self.__current,expr) )
|
|
elif (action == "include"):
|
|
self.__appendSegment( TemplateIncludeSegment(self,self.__current,expr) )
|
|
elif (action == "iterate"):
|
|
s = TemplateIterateSegment(self,self.__current,expr)
|
|
self.__appendGroup( s )
|
|
elif (action == "if"):
|
|
s = TemplateIfSegment(self,self.__current,expr)
|
|
self.__appendGroup( s )
|
|
elif (action == "end"):
|
|
self.__leaveGroup()
|
|
elif (action == "content"):
|
|
self.__appendSegment( TemplateContentSegment( self, self.__current ))
|
|
elif (action == "frame"):
|
|
self.__frame = expr
|
|
print("FRAME = {0} : {1}".format(expr, self.__frame))
|
|
# self.__frame = self.__provider.provide( expr )
|
|
else:
|
|
pass
|
|
|
|
|
|
def __parse(self,source):
|
|
if self.__root is None:
|
|
self.__root = TemplateGroupSegment(self)
|
|
self.__current = self.__root
|
|
pos = 0;
|
|
while (len(source) > pos):
|
|
iopen = None
|
|
iclose = None
|
|
|
|
try:
|
|
iopen = source.index( "<%", pos )
|
|
iclose = source.index( "%>", iopen )
|
|
|
|
except ValueError as ve:
|
|
self.debugException(ve)
|
|
if (iopen is None):
|
|
iopen = len(source)
|
|
iclose = len(source)
|
|
else:
|
|
raise ve
|
|
|
|
if (iopen > pos):
|
|
self.__appendSegment( TemplateTextSegment(self, self.__current, source[pos : iopen]) )
|
|
|
|
if (iopen < len(source)):
|
|
self.__parsetag( source[ iopen + 2 : iclose ] )
|
|
|
|
pos = iclose + 2
|
|
|
|
def debugException(self,ex):
|
|
pass
|
|
|
|
def __load_source(self):
|
|
if not self.template is None:
|
|
tfilename = "%s/%s" % (self.base,self.template)
|
|
st = os.stat( tfilename )
|
|
print("Template.__load_source(): Check %s > %s" % (st.st_mtime, self.__timestamp))
|
|
if (self.__timestamp is None) or (st.st_mtime > self.__timestamp):
|
|
self.__timestamp = st.st_mtime
|
|
tfile = open(tfilename,"r")
|
|
self.__source = tfile.read()
|
|
tfile.close()
|
|
self.__root = None
|
|
|
|
__allowed_builtins = ["getattr","setattr","hasattr","range","abs","dict","hex","int","float","bytes","bool","chr","ord","pow","str","map","round","len"]
|
|
|
|
def run(self,o = None,env = {},outfile=None,framed=None):
|
|
save_content = self.__content
|
|
|
|
if (not outfile is None):
|
|
self.__content = outfile
|
|
else:
|
|
self.__content = io.StringIO()
|
|
|
|
priv = {}
|
|
priv["framed"] = framed
|
|
|
|
self.__load_source()
|
|
self.__parse(self.__source)
|
|
|
|
if o is not None:
|
|
env['self'] = o
|
|
|
|
if not self.__disable_sandbox:
|
|
env['__builtins__'] = {
|
|
getattr: getattr
|
|
}
|
|
env['print'] = SandboxPrinter(self.__content)
|
|
|
|
|
|
env['include'] = SandboxInclude(self)
|
|
env['escape'] = html.escape
|
|
env['text2html'] = text2html
|
|
|
|
for an in self.__allowed_builtins:
|
|
if hasattr(globals(),an):
|
|
env[an] = getattr(globals(),an)
|
|
if hasattr(builtins,an):
|
|
env[an] = getattr(builtins,an)
|
|
|
|
print("Template.run(): my frame is: %s" % (self.__frame,))
|
|
|
|
self.env = env
|
|
if self.__frame is not None:
|
|
self.frame(env)
|
|
else:
|
|
self.__root.run(env,priv)
|
|
|
|
if (isinstance( self.__content, io.StringIO) ):
|
|
c = self.__content.getvalue()
|
|
else:
|
|
c = None
|
|
|
|
self.__content = save_content
|
|
return c
|
|
|
|
def frame(self,env):
|
|
fv = eval( self.__frame, env )
|
|
f = self.findTemplate( fv )
|
|
print("Template: Framing with %s" % (f,))
|
|
f.run(env=env,outfile=self.__content,framed=self)
|
|
|
|
def framed(self,env,priv):
|
|
print("Frame %s called %s" % (self.__frame,self))
|
|
self.__root.run(env,priv)
|
|
|
|
def include(self,expr,priv):
|
|
log("<!--INCLUDE: %s" % (expr,))
|
|
|
|
|
|
if isinstance(expr, Template):
|
|
|
|
cenv = dict( self.env )
|
|
self.__content.write( expr.run( None, cenv) )
|
|
|
|
elif isinstance(expr[0], Template):
|
|
|
|
cenv = dict( self.env )
|
|
self.__content.write( expr[0].run( expr[1], cenv) )
|
|
|
|
elif (not self.__provider is None) and (expr[0] in self.__provider):
|
|
|
|
child = self.__provider.provide( expr[0] )
|
|
cenv = dict( self.env )
|
|
self.__content.write( child.run( expr[1], cenv) )
|
|
|
|
else:
|
|
log("Template missing for <%include ...%>")
|
|
|
|
def findTemplate(self, expr):
|
|
if isinstance(expr, Template):
|
|
return expr
|
|
elif isinstance(expr[0], Template):
|
|
return expr[0]
|
|
elif (not self.__provider is None) and (expr[0] in self.__provider):
|
|
return self.__provider.provide( expr[0] )
|
|
else:
|
|
log("Template missing for <%include ...%>")
|
|
return None
|
|
|
|
|
|
def escape(self,t):
|
|
return t.replace("\\","\\\\").replace("\"","\\\"")
|
|
|
|
def __call__(self,request, o = None, output=None):
|
|
if output is None:
|
|
output = request.getContentFile()
|
|
self.run( o=o, env = { "request": request },outfile=output)
|
|
|