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

Source Code for Module PyFoam.Applications.PyFoamApplication

  1  #  ICE Revision: $Id: PyFoamApplication.py 12753 2013-01-03 23:08:03Z bgschaid $ 
  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 self.opts.developerMode: 217 self.opts.syntaxErrorDebugger=True 218 self.opts.keyboardInterrupTrace=True 219 self.opts.alsoCatchTERMsignal=True 220 self.opts.catchUSR1Signal=True 221 self.opts.interactiveDebug=True 222 self.opts.traceback=True 223 224 if self.opts.interactiveDebug: 225 sys.excepthook=lambda a1,a2,a3:pyFoamExceptionHook(a1, 226 a2, 227 a3, 228 debugOnSyntaxError=self.opts.syntaxErrorDebugger) 229 self.opts.traceback=True 230 if self.opts.catchUSR1Signal: 231 import signal 232 signal.signal(signal.SIGUSR1,pyFoamSIG1HandlerPrintStack) 233 if self.opts.alsoCatchTERMsignal: 234 signal.signal(signal.SIGTERM,pyFoamSIG1HandlerPrintStack) 235 self.opts.traceback=True 236 237 if self.opts.keyboardInterrupTrace: 238 import signal 239 signal.signal(signal.SIGINT,pyFoamSIG1HandlerPrintStack) 240 self.opts.traceback=True 241 242 if self.opts.psyco: 243 try: 244 import psyco 245 psyco.full() 246 except ImportError: 247 warning("No psyco installed. Continuing without acceleration") 248 249 if self.opts.profilePython or self.opts.profileCPython or self.opts.profileHotshot: 250 if sum([self.opts.profilePython,self.opts.profileCPython,self.opts.profileHotshot])>1: 251 self.error("Profiling with hotshot and regular profiling are mutual exclusive") 252 print_("Running profiled") 253 if self.opts.profilePython: 254 import profile 255 elif self.opts.profileCPython: 256 import cProfile as profile 257 else: 258 import hotshot 259 profileData=path.basename(sys.argv[0])+".profile" 260 if self.opts.profilePython or self.opts.profileCPython: 261 profile.runctx('self.run()',None,{'self':self},profileData) 262 print_("Reading python profile") 263 import pstats 264 stats=pstats.Stats(profileData) 265 else: 266 profileData+=".hotshot" 267 prof=hotshot.Profile(profileData) 268 prof.runctx('self.run()',{},{'self':self}) 269 print_("Writing and reading hotshot profile") 270 prof.close() 271 import hotshot.stats 272 stats=hotshot.stats.load(profileData) 273 stats.strip_dirs() 274 stats.sort_stats('time','calls') 275 stats.print_stats(20) 276 277 self.parser.restoreEnvironment() 278 else: 279 try: 280 if self.opts.pickleApplicationData=="stdout": 281 # Redirect output to memory 282 from PyFoam.ThirdParty.six.moves import StringIO 283 284 oldStdout=sys.stdout 285 oldStderr=sys.stderr 286 sys.stdout=StringIO() 287 sys.stderr=StringIO() 288 289 result=self.run() 290 291 # do this at the earliest possible moment 292 self.parser.restoreEnvironment() 293 294 if self.opts.pickleApplicationData=="stdout": 295 # restore stdout 296 self.__appData["stdout"]=sys.stdout.getvalue() 297 self.__appData["stderr"]=sys.stderr.getvalue() 298 sys.stdout=oldStdout 299 sys.stderr=oldStderr 300 301 if self.opts.pickleApplicationData: 302 from PyFoam.ThirdParty.six.moves import cPickle as pickle 303 if self.opts.pickleApplicationData=="stdout": 304 pick=pickle.Pickler(sys.stdout) 305 else: 306 pick=pickle.Pickler(open(self.opts.pickleApplicationData,'wb')) 307 pick.dump(self.__appData) 308 del pick 309 if self.opts.dumpAppData: 310 import pprint 311 print_("Application data:") 312 printer=pprint.PrettyPrinter() 313 printer.pprint(self.__appData) 314 315 return result 316 except PyFoamException: 317 e=sys.exc_info()[1] 318 if self.opts.traceback or self.calledAsClass: 319 raise 320 else: 321 self.errorPrint(str(e))
322
323 - def __getitem__(self,key):
324 """Get application data""" 325 try: 326 return self.__appData[key] 327 except KeyError: 328 print_("available keys:",list(self.__appData.keys())) 329 raise
330
331 - def __iter__(self):
332 """Iterate over the application data""" 333 for k in self.__appData: 334 yield k
335
336 - def iterkeys(self):
337 return iter(list(self.__appData.keys()))
338
339 - def iteritems(self):
340 return iter(list(self.__appData.items()))
341
342 - def getData(self):
343 """Get the application data""" 344 return deepcopy(self.__appData)
345
346 - def setData(self,data):
347 """Set the application data 348 349 @param data: dictionary whose entries will be added to the 350 application data (possibly overwriting old entries of the same name)""" 351 for k,v in iteritems(data): 352 self.__appData[k]=deepcopy(v)
353
354 - def ensureGeneralOptions(self):
355 if self.generalOpts==None: 356 self.generalOpts=OptionGroup(self.parser, 357 "General", 358 "General options for the control of OpenFOAM-runs") 359 self.parser.add_option_group(self.generalOpts)
360
361 - def addOptions(self):
362 """ 363 Add options to the parser 364 """ 365 pass
366
367 - def run(self):
368 """ 369 Run the real application 370 """ 371 error("Not a valid application")
372 373
374 - def error(self,*args):
375 """Raise a error exception. How it will be handled is a different story 376 @param args: Arguments to the exception 377 """ 378 raise PyFoamApplicationException(self,*args)
379
380 - def errorPrint(self,*args):
381 """ 382 Prints an error message and exits 383 @param args: Arguments that are to be printed 384 """ 385 if sys.stdout.isatty(): 386 print_(format.error, end=' ') 387 print_("Error in",self.calledName,":", end=' ') 388 for a in args: 389 print_(a, end=' ') 390 if sys.stdout.isatty(): 391 print_(format.reset) 392 sys.exit(-1)
393
394 - def warning(self,*args):
395 """ 396 Prints a warning message 397 @param args: Arguments that are to be printed 398 """ 399 if sys.stdout.isatty(): 400 print_(format.warn, end=' ') 401 print_("Warning in",self.calledName,":", end=' ') 402 for a in args: 403 print_(a, end=' ') 404 if sys.stdout.isatty(): 405 print_(format.reset)
406
407 - def silent(self,*args):
408 """ 409 Don't print a warning message 410 @param args: Arguments that are to be printed 411 """ 412 pass
413
414 - def checkCase(self,name,fatal=True,verbose=True):
415 """ 416 Check whether this is a valid OpenFOAM-case 417 @param name: the directory-bame that is supposed to be the case 418 @param fatal: If this is not a case then the application ends 419 @param verbose: If this is not a case no warning is issued 420 """ 421 if fatal: 422 func=self.error 423 elif verbose: 424 func=self.warning 425 else: 426 func=self.silent 427 428 if not path.exists(name): 429 func("Case",name,"does not exist") 430 return False 431 if not path.isdir(name): 432 func("Case",name,"is not a directory") 433 return False 434 if not path.exists(path.join(name,"system")): 435 func("Case",name,"does not have a 'system' directory") 436 return False 437 if not path.exists(path.join(name,"constant")): 438 func("Case",name,"does not have a 'constant' directory") 439 return False 440 441 return True
442
443 - def addToCaseLog(self,name,*text):
444 """ 445 Add information about the application that was run to the case-log 446 """ 447 448 logline=[NoTouchSolutionDirectory(name)] 449 if self.calledName==sys.argv[0]: 450 logline+=["Application:",path.basename(sys.argv[0])]+sys.argv[1:] 451 else: 452 logline+=["Application:",self.calledName] 453 454 logline+=[" | with cwd",getcwd()," | "] 455 logline+=text 456 NoTouchSolutionDirectory.addToHistory(*logline)
457
458 - def addLocalConfig(self,directory=None):
459 """ 460 Adds a local directory (assuming it is found) 461 """ 462 if directory!=None: 463 configuration().addFile(path.join(directory,"LocalConfigPyFoam"),silent=True)
464 465 # Should work with Python3 and Python2 466