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 "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
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
294 self.parser.restoreEnvironment()
295
296 if self.opts.pickleApplicationData=="stdout":
297
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
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
334 """Iterate over the application data"""
335 for k in self.__appData:
336 yield k
337
339 return iter(list(self.__appData.keys()))
340
342 return iter(list(self.__appData.items()))
343
345 """Get the application data"""
346 return deepcopy(self.__appData)
347
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
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
364 """
365 Add options to the parser
366 """
367 pass
368
370 """
371 Run the real application
372 """
373 error("Not a valid application")
374
375
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
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
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
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
459
466
467
468