1
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
32
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
52
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
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
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
292 self.parser.restoreEnvironment()
293
294 if self.opts.pickleApplicationData=="stdout":
295
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
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
332 """Iterate over the application data"""
333 for k in self.__appData:
334 yield k
335
337 return iter(list(self.__appData.keys()))
338
340 return iter(list(self.__appData.items()))
341
343 """Get the application data"""
344 return deepcopy(self.__appData)
345
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
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
362 """
363 Add options to the parser
364 """
365 pass
366
368 """
369 Run the real application
370 """
371 error("Not a valid application")
372
373
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
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
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
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
457
464
465
466