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