python-hserver/hserver/templates/Template.py

462 lines
12 KiB
Python

import io
import re
import sys
import os
from hserver.api.WebCallable import WebCallable
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 TemplateUseSegment(TemplateSegment):
def __init__(self,template,parent = None,code = ""):
TemplateSegment.__init__(self,template,parent)
print("USE: {0}".format(code))
(self._name,self._arguments) = code.split(" ",1)
def name(self):
return self._name
def run(self,env,priv={}):
args = eval(self._arguments, env)
tmpl = self.getTemplate().getDefine( self._name )
e = dict( env )
if isinstance( args, dict ):
e.update( args )
else:
e["args"] = args
tmpl.use(e,priv)
def __repr__(self):
return "<TemplateUseSegment( %s=%s : %s )>" % (self.__iterator,self.__iteration,self.getChildren(),)
class TemplateDefineSegment(TemplateGroupSegment):
def __init__(self,template,parent = None,code = ""):
TemplateGroupSegment.__init__(self,template,parent)
self._name = code
template.addDefine( self )
def name(self):
return self._name
def run(self,env,priv={}):
pass
def use(self,env,priv={}):
TemplateGroupSegment.run(self,env,priv)
def __repr__(self):
return "<TemplateDefineSegment( %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(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
self.__defines = {}
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 addDefine(self,define):
self.__defines[ define.name() ] = define
def getDefine(self,name):
return self.__defines[ name ]
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 == "define"):
s = TemplateDefineSegment(self,self.__current,expr)
self.__appendGroup( s )
elif (action == "end"):
self.__leaveGroup()
elif (action == "content"):
self.__appendSegment( TemplateContentSegment( self, self.__current ))
elif (action == "use"):
self.__appendSegment( TemplateUseSegment( self, self.__current, expr ))
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):
log("findTemplate({0})".format(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 <%frame ...%>")
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)