Package PyFoam :: Package Applications :: Module PyFoamApplication
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.Applications.PyFoamApplication

  1  #  ICE Revision: $Id$ 
  2  """Base class for pyFoam-applications 
  3   
  4  Classes can also be called with a command-line string""" 
  5   
  6  from optparse import OptionGroup 
  7  from PyFoam.Basics.FoamOptionParser import FoamOptionParser 
  8  from PyFoam.Error import error,warning,FatalErrorPyFoamException,PyFoamException 
  9  from PyFoam.RunDictionary.SolutionDirectory import NoTouchSolutionDirectory 
 10   
 11  from PyFoam.Basics.TerminalFormatter import TerminalFormatter 
 12  from PyFoam import configuration 
 13   
 14  format=TerminalFormatter() 
 15  format.getConfigFormat("error") 
 16  format.getConfigFormat("warn") 
 17   
 18  import sys 
 19  from os import path,getcwd,environ 
 20  from copy import deepcopy 
 21   
 22  from PyFoam.ThirdParty.six import print_ 
 23  from PyFoam.ThirdParty.six import iteritems 
 24   
25 -class PyFoamApplicationException(FatalErrorPyFoamException):
26 - def __init__(self,app,*text):
29
30 - def __str__(self):
31 return FatalErrorPyFoamException.__str__(self)+" in Application-class: "+self.app.__class__.__name__
32
33 -def pyFoamExceptionHook(type,value,tb,debugOnSyntaxError=False):
34 if hasattr(sys,'ps1'): 35 warning("Interactive mode. No debugger") 36 sys.__excepthook__(type,value,tb) 37 elif not (sys.stderr.isatty() and sys.stdin.isatty() and sys.stdout.isatty()): 38 warning("Not on a terminal. No debugger") 39 sys.__excepthook__(type,value,tb) 40 elif issubclass(type,SyntaxError) and not debugOnSyntaxError: 41 warning("Syntax error. No debugger") 42 sys.__excepthook__(type,value,tb) 43 else: 44 import traceback,pdb 45 traceback.print_exception(type,value,tb) 46 print_() 47 pdb.pm()
48
49 -def pyFoamSIG1HandlerPrintStack(nr,frame):
50 print_("Signal Nr",nr,"sent") 51 raise FatalErrorPyFoamException("Signal nr",nr,"sent")
52
53 -class PyFoamApplication(object):
54 - def __init__(self, 55 args=None, 56 description=None, 57 usage=None, 58 interspersed=False, 59 nr=None, 60 changeVersion=True, 61 exactNr=True, 62 inputApp=None):
63 """ 64 @param description: description of the command 65 @param usage: Usage 66 @param interspersed: Is the command line allowed to be interspersed (options after the arguments) 67 @param args: Command line arguments when using the Application as a 'class' from a script 68 @param nr: Number of required arguments 69 @param changeVersion: May this application change the version of OF used? 70 @param exactNr: Must not have more than the required number of arguments 71 @param inputApp: Application with input data. Used to allow a 'pipe-like' behaviour if the class is used from a Script 72 """ 73 self.parser=FoamOptionParser(args=args, 74 description=description, 75 usage=usage, 76 interspersed=interspersed) 77 78 self.calledName=sys.argv[0] 79 self.calledAsClass=(args!=None) 80 if self.calledAsClass: 81 self.calledName=self.__class__.__name__+" used by "+sys.argv[0] 82 self.parser.prog=self.calledName 83 84 self.generalOpts=None 85 86 self.__appData={} 87 if inputApp: 88 self.__appData["inputData"]=inputApp.getData() 89 90 grp=OptionGroup(self.parser, 91 "Default", 92 "Options common to all PyFoam-applications") 93 94 if changeVersion: 95 # the options are evaluated in Basics.FoamOptionParser 96 grp.add_option("--foamVersion", 97 dest="foamVersion", 98 default=None, 99 help="Change the OpenFOAM-version that is to be used") 100 if "WM_PROJECT_VERSION" in environ: 101 grp.add_option("--currentFoamVersion", 102 dest="foamVersion", 103 const=environ["WM_PROJECT_VERSION"], 104 default=None, 105 action="store_const", 106 help="Use the current OpenFOAM-version "+environ["WM_PROJECT_VERSION"]) 107 108 grp.add_option("--force-32bit", 109 dest="force32", 110 default=False, 111 action="store_true", 112 help="Forces the usage of a 32-bit-version if that version exists as 32 and 64 bit. Only used when --foamVersion is used") 113 grp.add_option("--force-64bit", 114 dest="force64", 115 default=False, 116 action="store_true", 117 help="Forces the usage of a 64-bit-version if that version exists as 32 and 64 bit. Only used when --foamVersion is used") 118 grp.add_option("--force-debug", 119 dest="compileOption", 120 const="Debug", 121 default=None, 122 action="store_const", 123 help="Forces the value Debug for the WM_COMPILE_OPTION. Only used when --foamVersion is used") 124 grp.add_option("--force-opt", 125 dest="compileOption", 126 const="Opt", 127 default=None, 128 action="store_const", 129 help="Forces the value Opt for the WM_COMPILE_OPTION. Only used when --foamVersion is used") 130 131 grp.add_option("--psyco-accelerated", 132 dest="psyco", 133 default=False, 134 action="store_true", 135 help="Accelerate the script using the psyco-library (EXPERIMENTAL and requires a separatly installed psyco)") 136 grp.add_option("--profile-python", 137 dest="profilePython", 138 default=False, 139 action="store_true", 140 help="Profile the python-script (not the OpenFOAM-program) - mostly of use for developers") 141 grp.add_option("--profile-cpython", 142 dest="profileCPython", 143 default=False, 144 action="store_true", 145 help="Profile the python-script (not the OpenFOAM-program) using the better cProfile library - mostly of use for developers") 146 grp.add_option("--profile-hotshot", 147 dest="profileHotshot", 148 default=False, 149 action="store_true", 150 help="Profile the python-script using the hotshot-library (not the OpenFOAM-program) - mostly of use for developers - EXPERIMENTAL") 151 152 dbg=OptionGroup(self.parser, 153 "Debugging", 154 "Options mainly used for debugging PyFoam-Utilities") 155 156 dbg.add_option("--traceback-on-error", 157 dest="traceback", 158 default=False, 159 action="store_true", 160 help="Prints a traceback when an error is encountered (for debugging)") 161 dbg.add_option("--interactive-debugger", 162 dest="interactiveDebug", 163 default=False, 164 action="store_true", 165 help="In case of an exception start the interactive debugger PDB. Also implies --traceback-on-error") 166 dbg.add_option("--catch-USR1-signal", 167 dest="catchUSR1Signal", 168 default=False, 169 action="store_true", 170 help="If the USR1-signal is sent to the application with 'kill -USR1 <pid>' the application ens and prints a traceback. If interactive debugging is enabled then the debugger is entered. Use to investigate hangups") 171 dbg.add_option("--also-catch-TERM-signal", 172 dest="alsoCatchTERMsignal", 173 default=False, 174 action="store_true", 175 help="In addition to USR1 catch the regular TERM-kill") 176 dbg.add_option("--keyboard-interrupt-trace", 177 dest="keyboardInterrupTrace", 178 default=False, 179 action="store_true", 180 help="Make the application behave like with --catch-USR1-signal if <Ctrl>-C is pressed") 181 dbg.add_option("--syntax-error-debugger", 182 dest="syntaxErrorDebugger", 183 default=False, 184 action="store_true", 185 help="Only makes sense with --interactive-debugger: Do interactive debugging even when a syntax error was encountered") 186 dbg.add_option("--i-am-a-developer", 187 dest="developerMode", 188 default=False, 189 action="store_true", 190 help="Switch on all of the above options. Usually this mkes only sense if you're developing PyFoam'") 191 192 grp.add_option("--dump-application-data", 193 dest="dumpAppData", 194 default=False, 195 action="store_true", 196 help="Print the dictionary with the generated application data after running") 197 grp.add_option("--pickle-application-data", 198 dest="pickleApplicationData", 199 default=None, 200 action="store", 201 type="string", 202 help="""\ 203 Write a pickled version of the application data to a file. If the 204 filename given is 'stdout' then the pickled data is written to 205 stdout. The usual standard output is then captured and added to the 206 application data as an entry 'stdout' (same for 'stderr'). Be careful 207 with these option for commands that generate a lot of output""") 208 209 self.parser.add_option_group(grp) 210 self.parser.add_option_group(dbg) 211 212 self.addOptions() 213 self.parser.parse(nr=nr,exactNr=exactNr) 214 self.opts=self.parser.getOptions() 215 216 if "WM_PROJECT_VERSION" not in environ: 217 warning("$WM_PROJECT_VERSION unset. PyFoam will not be able to determine the OpenFOAM-version and behave strangely") 218 if self.opts.developerMode: 219 self.opts.syntaxErrorDebugger=True 220 self.opts.keyboardInterrupTrace=True 221 self.opts.alsoCatchTERMsignal=True 222 self.opts.catchUSR1Signal=True 223 self.opts.interactiveDebug=True 224 self.opts.traceback=True 225 226 if self.opts.interactiveDebug: 227 sys.excepthook=lambda a1,a2,a3:pyFoamExceptionHook(a1, 228 a2, 229 a3, 230 debugOnSyntaxError=self.opts.syntaxErrorDebugger) 231 self.opts.traceback=True 232 if self.opts.catchUSR1Signal: 233 import signal 234 signal.signal(signal.SIGUSR1,pyFoamSIG1HandlerPrintStack) 235 if self.opts.alsoCatchTERMsignal: 236 signal.signal(signal.SIGTERM,pyFoamSIG1HandlerPrintStack) 237 self.opts.traceback=True 238 239 if self.opts.keyboardInterrupTrace: 240 import signal 241 signal.signal(signal.SIGINT,pyFoamSIG1HandlerPrintStack) 242 self.opts.traceback=True 243 244 if self.opts.psyco: 245 try: 246 import psyco 247 psyco.full() 248 except ImportError: 249 warning("No psyco installed. Continuing without acceleration") 250 251 if self.opts.profilePython or self.opts.profileCPython or self.opts.profileHotshot: 252 if sum([self.opts.profilePython,self.opts.profileCPython,self.opts.profileHotshot])>1: 253 self.error("Profiling with hotshot and regular profiling are mutual exclusive") 254 print_("Running profiled") 255 if self.opts.profilePython: 256 import profile 257 elif self.opts.profileCPython: 258 import cProfile as profile 259 else: 260 import hotshot 261 profileData=path.basename(sys.argv[0])+".profile" 262 if self.opts.profilePython or self.opts.profileCPython: 263 profile.runctx('self.run()',None,{'self':self},profileData) 264 print_("Reading python profile") 265 import pstats 266 stats=pstats.Stats(profileData) 267 else: 268 profileData+=".hotshot" 269 prof=hotshot.Profile(profileData) 270 prof.runctx('self.run()',{},{'self':self}) 271 print_("Writing and reading hotshot profile") 272 prof.close() 273 import hotshot.stats 274 stats=hotshot.stats.load(profileData) 275 stats.strip_dirs() 276 stats.sort_stats('time','calls') 277 stats.print_stats(20) 278 279 self.parser.restoreEnvironment() 280 else: 281 try: 282 if self.opts.pickleApplicationData=="stdout": 283 # Redirect output to memory 284 from PyFoam.ThirdParty.six.moves import StringIO 285 286 oldStdout=sys.stdout 287 oldStderr=sys.stderr 288 sys.stdout=StringIO() 289 sys.stderr=StringIO() 290 291 result=self.run() 292 293 # do this at the earliest possible moment 294 self.parser.restoreEnvironment() 295 296 if self.opts.pickleApplicationData=="stdout": 297 # restore stdout 298 self.__appData["stdout"]=sys.stdout.getvalue() 299 self.__appData["stderr"]=sys.stderr.getvalue() 300 sys.stdout=oldStdout 301 sys.stderr=oldStderr 302 303 if self.opts.pickleApplicationData: 304 from PyFoam.ThirdParty.six.moves import cPickle as pickle 305 if self.opts.pickleApplicationData=="stdout": 306 pick=pickle.Pickler(sys.stdout) 307 else: 308 pick=pickle.Pickler(open(self.opts.pickleApplicationData,'wb')) 309 pick.dump(self.__appData) 310 del pick 311 if self.opts.dumpAppData: 312 import pprint 313 print_("Application data:") 314 printer=pprint.PrettyPrinter() 315 printer.pprint(self.__appData) 316 317 return result 318 except PyFoamException: 319 e=sys.exc_info()[1] 320 if self.opts.traceback or self.calledAsClass: 321 raise 322 else: 323 self.errorPrint(str(e))
324
325 - def __getitem__(self,key):
326 """Get application data""" 327 try: 328 return self.__appData[key] 329 except KeyError: 330 print_("available keys:",list(self.__appData.keys())) 331 raise
332
333 - def __iter__(self):
334 """Iterate over the application data""" 335 for k in self.__appData: 336 yield k
337
338 - def iterkeys(self):
339 return iter(list(self.__appData.keys()))
340
341 - def iteritems(self):
342 return iter(list(self.__appData.items()))
343
344 - def getData(self):
345 """Get the application data""" 346 return deepcopy(self.__appData)
347
348 - def setData(self,data):
349 """Set the application data 350 351 @param data: dictionary whose entries will be added to the 352 application data (possibly overwriting old entries of the same name)""" 353 for k,v in iteritems(data): 354 self.__appData[k]=deepcopy(v)
355
356 - def ensureGeneralOptions(self):
357 if self.generalOpts==None: 358 self.generalOpts=OptionGroup(self.parser, 359 "General", 360 "General options for the control of OpenFOAM-runs") 361 self.parser.add_option_group(self.generalOpts)
362
363 - def addOptions(self):
364 """ 365 Add options to the parser 366 """ 367 pass
368
369 - def run(self):
370 """ 371 Run the real application 372 """ 373 error("Not a valid application")
374 375
376 - def error(self,*args):
377 """Raise a error exception. How it will be handled is a different story 378 @param args: Arguments to the exception 379 """ 380 raise PyFoamApplicationException(self,*args)
381
382 - def errorPrint(self,*args):
383 """ 384 Prints an error message and exits 385 @param args: Arguments that are to be printed 386 """ 387 if sys.stdout.isatty(): 388 print_(format.error, end=' ') 389 print_("Error in",self.calledName,":", end=' ') 390 for a in args: 391 print_(a, end=' ') 392 if sys.stdout.isatty(): 393 print_(format.reset) 394 sys.exit(-1)
395
396 - def warning(self,*args):
397 """ 398 Prints a warning message 399 @param args: Arguments that are to be printed 400 """ 401 if sys.stdout.isatty(): 402 print_(format.warn, end=' ') 403 print_("Warning in",self.calledName,":", end=' ') 404 for a in args: 405 print_(a, end=' ') 406 if sys.stdout.isatty(): 407 print_(format.reset)
408
409 - def silent(self,*args):
410 """ 411 Don't print a warning message 412 @param args: Arguments that are to be printed 413 """ 414 pass
415
416 - def checkCase(self,name,fatal=True,verbose=True):
417 """ 418 Check whether this is a valid OpenFOAM-case 419 @param name: the directory-bame that is supposed to be the case 420 @param fatal: If this is not a case then the application ends 421 @param verbose: If this is not a case no warning is issued 422 """ 423 if fatal: 424 func=self.error 425 elif verbose: 426 func=self.warning 427 else: 428 func=self.silent 429 430 if not path.exists(name): 431 func("Case",name,"does not exist") 432 return False 433 if not path.isdir(name): 434 func("Case",name,"is not a directory") 435 return False 436 if not path.exists(path.join(name,"system")): 437 func("Case",name,"does not have a 'system' directory") 438 return False 439 if not path.exists(path.join(name,"constant")): 440 func("Case",name,"does not have a 'constant' directory") 441 return False 442 443 return True
444
445 - def addToCaseLog(self,name,*text):
446 """ 447 Add information about the application that was run to the case-log 448 """ 449 450 logline=[NoTouchSolutionDirectory(name)] 451 if self.calledName==sys.argv[0]: 452 logline+=["Application:",path.basename(sys.argv[0])]+sys.argv[1:] 453 else: 454 logline+=["Application:",self.calledName] 455 456 logline+=[" | with cwd",getcwd()," | "] 457 logline+=text 458 NoTouchSolutionDirectory.addToHistory(*logline)
459
460 - def addLocalConfig(self,directory=None):
461 """ 462 Adds a local directory (assuming it is found) 463 """ 464 if directory!=None: 465 configuration().addFile(path.join(directory,"LocalConfigPyFoam"),silent=True)
466 467 # Should work with Python3 and Python2 468