Package PyFoam :: Package Basics :: Module TemplateFile
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.Basics.TemplateFile

  1  #  ICE Revision: $Id$ 
  2   
  3  import re 
  4  from math import * 
  5  import sys 
  6   
  7  from PyFoam.Error import error,warning 
  8  from PyFoam.ThirdParty.pyratemp import Template as PyratempTemplate 
  9  from PyFoam.ThirdParty.pyratemp import EvalPseudoSandbox,TemplateRenderError 
 10  from PyFoam.ThirdParty.pyratemp import Renderer as PyratempRenderer 
 11   
 12  from PyFoam.ThirdParty.six import iteritems,exec_,print_ 
 13   
14 -class RendererWithFilename(PyratempRenderer):
15 """Usual renderer but report a filename""" 16
17 - def __init__(self, evalfunc, escapefunc,filename=None):
18 PyratempRenderer.__init__(self, evalfunc, escapefunc) 19 20 self.fileName = filename
21
22 - def reportString(self,expr, err):
23 result="Cannot eval expression '%s'. (%s: %s)" %(expr, err.__class__.__name__, err) 24 if self.fileName: 25 result+=" in file "+self.fileName 26 return result
27
28 - def _eval(self, expr, data):
29 """evalfunc with error-messages""" 30 try: 31 return self.evalfunc(expr, data) 32 except (TypeError,NameError,IndexError,KeyError,AttributeError, SyntaxError): 33 err = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e' 34 raise TemplateRenderError(self.reportString(expr,err))
35
36 -class TolerantRenderer(RendererWithFilename):
37 """Variant of the renderer that doesn't choke on problems with evaluations""" 38
39 - def __init__(self, evalfunc, escapefunc,filename=None):
40 RendererWithFilename.__init__(self, evalfunc, escapefunc,filename=filename)
41
42 - def _eval(self, expr, data):
43 """evalfunc with error-messages""" 44 try: 45 return self.evalfunc(expr, data) 46 except (TypeError,NameError,IndexError,KeyError,AttributeError, SyntaxError): 47 err = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e' 48 warning(self.reportString(expr,err)) 49 return "Template evaluation ERROR: "+self.reportString(expr,err)
50 51 execIdString="this is meant to be executed:" 52 substituteIdString="substitute current values into this string:" 53
54 -class PyratempPreprocessor(object):
55 """This class preprocesses the input that is give to it in such a 56 way that the old format (using $$ at the line beginnings and $ 57 .. $ for expressions) is reworked into something that pyratemp understands 58 """
59 - def __init__(self, 60 dovarline=True, 61 doexpr=True, 62 expressionDelimiter="$", 63 assignmentLineStart="$$", 64 allowExec=False, 65 assignmentDebug=None, 66 specials=[]):
67 """Create the regexp once for performance reasons 68 @param dovarline: look for variable lines that start with $$ 69 @param doexpr: substitute expressions that are between $ 70 @param expressionDelimiter: character/string that is used before and after an 71 expression. After the expression the reverse of the string is used 72 @param assignmentLineStart: character sequence that signals an assignment line 73 @param assignmentDebug: Add a commented line to debug assignments. Prefix used is this parameter 74 @param allowExec: allows execution of code. This is potentially unsafe 75 @param specials: a list. If any expression starts with one of these values then 76 the full expression (including delimiters) is left verbatim in the template""" 77 78 self.clip=len(expressionDelimiter) 79 self.specials=specials 80 81 tmp=list(expressionDelimiter) 82 tmp.reverse() 83 84 self.expressionDelimiter=re.escape(expressionDelimiter) 85 self.expressionDelimiterEnd=re.escape("".join(tmp)) 86 self.expressionDelimiterRaw=expressionDelimiter 87 self.expressionDelimiterEndRaw="".join(tmp) 88 89 # print self.expressionDelimiter,self.expressionDelimiterEnd 90 91 self.assignmentLineStart=assignmentLineStart 92 self.assignmentDebug=assignmentDebug 93 94 self.expr=re.compile("%s[^$!\n]+?%s" % (self.expressionDelimiter,self.expressionDelimiterEnd)) 95 self.dovarline=dovarline 96 self.doexpr=doexpr 97 98 self.allowExec=allowExec
99
100 - def __call__(self,original):
101 """This does the actual work""" 102 103 if len(original)==0: 104 return original 105 106 lines=original.split("\n") 107 if lines[-1]=="": 108 lines=lines[:-1] 109 110 result="" 111 112 for l in lines: 113 if l[:len(self.assignmentLineStart)]==self.assignmentLineStart and self.dovarline: 114 tmp=l[len(self.assignmentLineStart):].split("=") 115 if len(tmp)!=2: 116 if self.allowExec: 117 execString=l[len(self.assignmentLineStart):].replace("\\","\\\\").replace("\"","\\\"") 118 result+='$!setvar("%s", "%s")!$#!' % ( 119 "dummyVarForExecution", 120 execIdString+execString.strip() 121 ) 122 result+="\n" 123 else: 124 error("Each definition must be of the form: <name>=<value>", 125 "The string",l,"is not") 126 else: 127 # if tmp[1].find('"')>=0: 128 # error("There is a \" in",tmp[1],"\npyratemp can't cope with that'") 129 exprStr=tmp[1].replace("\\","\\\\").replace("\"","\\\"") 130 result+='$!setvar("%s", "%s")!$#!' % (tmp[0].strip(),exprStr.strip()) 131 result+="\n" 132 if self.assignmentDebug and self.doexpr: 133 l=self.assignmentDebug+" "+tmp[0].strip()+" "+self.expressionDelimiterRaw+tmp[0].strip()+self.expressionDelimiterEndRaw 134 else: 135 continue 136 if self.doexpr: 137 nl="" 138 iStart=0 139 for m in self.expr.finditer(l): 140 inner=l[m.start()+self.clip:m.end()-self.clip] 141 hasSpecial=False 142 nl+=l[iStart:m.start()] 143 for k in self.specials: 144 if len(k)<=len(inner): 145 if inner[:len(k)]==k: 146 hasSpecial=True 147 substVarName="dummyVarForSubstitution" 148 # nl+=l[m.start():m.end()] 149 nl+='$!setvar("%s", "%s")!$#!\n' % ( 150 substVarName, 151 substituteIdString+l[m.start():m.end()] 152 ) 153 nl+='$!'+substVarName+'!$' 154 155 if not hasSpecial: 156 nl+="$!"+inner+"!$" 157 iStart=m.end() 158 result+=nl+l[iStart:]+"\n" 159 else: 160 result+=l+"\n" 161 162 # remove trailing newline if the original had none 163 if original[-1]!='\n' and result[-1]=='\n': 164 result=result[:-1] 165 166 return result
167
168 -class TemplateFileOldFormat(object):
169 """Works on template files. Does calculations between $$. 170 Lines that start with $$ contain definitions""" 171
172 - def __init__(self,name=None,content=None):
173 """Exactly one of the parameters must be specified 174 @param name: name of the template file. 175 @param content: Content of the template""" 176 if name==None and content==None: 177 error("Either a file name or the content of the template must be specified") 178 if name!=None and content!=None: 179 error("Both: a file name and the content of the template were specified") 180 if content!=None: 181 template=content 182 else: 183 template=open(name).read() 184 self.buildTemplate(template)
185
186 - def buildTemplate(self,template):
187 lines=template.split("\n") 188 self.expressions={} 189 self.template="" 190 for l in lines: 191 if l[:2]!="$$": 192 self.template+=l+"\n" 193 else: 194 tmp=l[2:].split("=") 195 if len(tmp)!=2: 196 error("Each definition must be of the form: <name>=<value>", 197 "The string",l,"is not") 198 self.expressions[tmp[0].strip()]=tmp[1]
199
200 - def writeToFile(self,outfile,vals):
201 """In the template, replaces all the strings between $$ 202 with the evaluation of the expressions and writes the results to a file 203 @param outfile: the resulting output file 204 @param vals: dictionary with the values""" 205 206 output=self.getString(vals) 207 208 open(outfile,"w").write(output)
209
210 - def getString(self,vals):
211 """In the template, replaces all the strings between $$ 212 with the evaluation of the expressions 213 @param vals: dictionary with the values 214 @returns: The string with the replaced expressions""" 215 216 symbols=vals.copy() 217 218 exp=re.compile("\$[^$\n]*\$") 219 220 for n,e in iteritems(self.expressions): 221 if n in vals: 222 error("Key",n,"already existing in",vals) 223 symbols[n]="("+str(e)+")" 224 225 keys=list(symbols.keys()) 226 227 keys.sort(key=len,reverse=True) 228 229 input=self.template[:] 230 m=exp.search(input) 231 while m: 232 a,e=m.span() 233 pre=input[0:a] 234 post=input[e:] 235 mid=input[a+1:e-1] 236 237 old="" 238 while old!=mid: 239 old=mid 240 for k in keys: 241 if mid.find(k)>=0: 242 mid=mid.replace(k,str(symbols[k])) 243 break 244 245 try: 246 input=pre+str(eval(mid))+post 247 except ArithmeticError: 248 e = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e' 249 print_("Problem evaluating",mid) 250 raise e 251 252 m=exp.search(input) 253 254 return input
255
256 -class EvalPseudoSandboxWithMath(EvalPseudoSandbox):
257 """Add mathematical functions to the valid functons"""
258 - def __init__(self,allowExec=False):
259 EvalPseudoSandbox.__init__(self) 260 import math 261 for o in dir(math): 262 if o[0]!="_": 263 self.register(o,getattr(math,o)) 264 265 from PyFoam.ThirdParty.six.moves import builtins as __builtin__ 266 self.register("set",__builtin__.set) 267 268 if allowExec: 269 del self.eval_allowed_globals["__import__"] 270 self.register("__import__",__builtins__["__import__"])
271
272 - def compile(self, expr,mode="eval"):
273 """Compile a python-eval-expression. Overrides the default implementation 274 to allow '_[1]' as a valid name 275 """ 276 if expr not in self._compile_cache: 277 c = compile(expr, "", mode) 278 for i in c.co_names: #prevent breakout via new-style-classes 279 if i[0] == '_': 280 if i[1]!='[' or i[-1]!=']': 281 raise NameError("Name '%s' is not allowed." %(i)) 282 self._compile_cache[expr] = c 283 return self._compile_cache[expr]
284
285 - def eval(self, expr, locals):
286 """Eval a python-eval-expression. 287 288 Sets ``self.locals_ptr`` to ``locales`` and compiles the code 289 before evaluating. 290 """ 291 292 if expr[:len(substituteIdString)]==substituteIdString: 293 goOn=True 294 replacement=expr[len(substituteIdString):] 295 while goOn: 296 try: 297 value=replacement % locals 298 goOn=False 299 except KeyError: 300 e = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e' 301 kExpr="%("+e.args[0]+")" 302 replacement=replacement.replace(kExpr,"%"+kExpr) 303 304 return value 305 # print value 306 307 sav = self.locals_ptr 308 self.locals_ptr = locals 309 doEval=True 310 311 if expr[:len(execIdString)]==execIdString: 312 doEval=False 313 314 if doEval: 315 globals= {"__builtins__":self.eval_allowed_globals} 316 x = eval(self.compile(expr),globals, locals) 317 else: 318 # globals= {"__builtins__":self.eval_allowed_globals} 319 globals= {"__builtins__":__builtins__} 320 expr=expr[len(execIdString):] 321 exec_(self.compile(expr,mode="exec"),globs=globals,locs=locals) 322 x = None 323 self.locals_ptr = sav 324 return x
325
326 -class EvalPseudoSandboxWithMathWithImport(EvalPseudoSandboxWithMath):
327 """Class that allows the import of packages"""
328 - def __init__(self):
329 EvalPseudoSandboxWithMath.__init__(self,allowExec=True)
330
331 -class TemplateFile(TemplateFileOldFormat):
332 """Works on template files. Does calculations between $$. 333 Lines that start with $$ contain definitions""" 334
335 - def __init__(self, 336 name=None, 337 content=None, 338 encoding="utf-8", 339 expressionDelimiter="|", 340 assignmentLineStart="$$", 341 assignmentDebug=None, 342 specials=[], 343 renderer_class=None, 344 tolerantRender=False, 345 allowExec=False 346 ):
347 """Exactly one of the parameters must be specified 348 @param name: name of the template file. 349 @param content: Content of the template 350 @param expressionDelimiter: character/string that delimits expression strings. 351 @param assignmentLineStart: Start of a line that holds an assignment operation 352 @param assignmentDebug: Add a commented line to debug assignments. Prefix used is this parameter 353 @param allowExec: allow execution (and import). This is potentially unsafe 354 @param special: list with strings that leave expression untreated""" 355 356 self.expressionDelimiter=expressionDelimiter 357 self.assignmentLineStart=assignmentLineStart 358 self.assignmentDebug=assignmentDebug 359 self.specials=specials 360 self.allowExec=allowExec 361 362 super(TemplateFile,self).__init__(name=name, 363 content=content, 364 ) 365 366 if renderer_class==None: 367 if tolerantRender: 368 class ConcreteTolerantRenderer(TolerantRenderer): 369 def __init__(self,evalfunc, escapefunc): 370 TolerantRenderer.__init__(self, 371 evalfunc, 372 escapefunc,filename=name)
373 374 renderer_class=ConcreteTolerantRenderer 375 else: 376 class ConcreteRenderWithFileName(RendererWithFilename): 377 def __init__(self,evalfunc, escapefunc): 378 RendererWithFilename.__init__(self, 379 evalfunc, 380 escapefunc,filename=name) 381 382 renderer_class=ConcreteRenderWithFileName 383 384 if allowExec: 385 sandbox=EvalPseudoSandboxWithMathWithImport 386 else: 387 sandbox=EvalPseudoSandboxWithMath 388 389 self.ptemplate=PyratempTemplate(string=self.template, 390 eval_class=sandbox, 391 renderer_class=renderer_class, 392 encoding=encoding, 393 escape=None 394 ) 395
396 - def buildTemplate(self,template):
397 self.template=PyratempPreprocessor(assignmentLineStart=self.assignmentLineStart, 398 expressionDelimiter=self.expressionDelimiter, 399 assignmentDebug=self.assignmentDebug, 400 specials=self.specials, 401 allowExec=self.allowExec 402 )(template)
403
404 - def getString(self,vals):
405 """In the template, replaces all the strings between $$ 406 with the evaluation of the expressions 407 @param vals: dictionary with the values 408 @returns: The string with the replaced expressions""" 409 410 return self.ptemplate(**vals)
411 412 # Should work with Python3 and Python2 413