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

Source Code for Module PyFoam.Applications.PyFoamApplication

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