1
2 """Parse options for the PyFoam-Scripts"""
3
4 from optparse import OptionParser,TitledHelpFormatter
5 import textwrap
6
7 from PyFoam import versionString
8
9 from PyFoam.FoamInformation import changeFoamVersion
10 from PyFoam.FoamInformation import oldAppConvention as oldApp
11
12 from PyFoam.Error import error,warning
13 from PyFoam.ThirdParty.six import iteritems
14 from PyFoam.ThirdParty.six import string_types,integer_types
15
16 from os import path,environ
17 from copy import deepcopy
18
34
36 """Wrapper to the usual OptionParser to honor the conventions of OpenFOAM-utilities
37
38 Options that are not used by the script are passed to the OpenFOAM-application"""
39
40 - def __init__(self,
41 args=None,
42 usage=None,
43 version=None,
44 description=None,
45 epilog=None,
46 examples=None,
47 interspersed=False):
48 """
49 @param usage: usage string. If missing a default is used
50 @param version: if missing the PyFoam-version is used
51 @param description: description of the utility
52 @param epilog: Text to be displayed in the help after the options
53 @param examples: Usage examples to be displayed after the epilog
54 @param interspersed: needs to be false if options should be passed to an OpenFOAM-utility
55 @param args: Command line arguments. If unset sys.argv[1:] is used.
56 Can be a string: it will be splitted then unsing the spaces (very primitive), or a list of strings (prefered)
57 """
58 if usage==None:
59 if oldApp():
60 usage="%prog [options] <foamApplication> <caseDir> <caseName> [foamOptions]"
61 else:
62 usage="%prog [options] <foamApplication> [foamOptions]"
63
64 if version==None:
65 version="%prog "+versionString()
66
67 if args==None:
68 self.argLine=None
69 elif type(args)==str:
70 self.argLine=args.split()
71 else:
72 self.argLine=map(str,args)
73
74 if examples:
75 if epilog is None:
76 epilog=""
77 else:
78 epilog+="\n\n"
79 usageText="Usage examples:"
80
81 epilog+=usageText+"\n\n"+examples
82
83 OptionParser.__init__(self,
84 usage=usage,
85
86 version=version,
87 description=description,
88 epilog=epilog,
89 formatter=FoamHelpFormatter())
90
91 if self.epilog:
92 self.epilog=self.expand_prog_name(self.epilog)
93
94 if interspersed:
95 self.enable_interspersed_args()
96 else:
97 self.disable_interspersed_args()
98
99 self.options=None
100 self.args=None
101
102 self.__foamVersionChanged=False
103 self.__oldEnvironment=None
104
106 """Restore the environment to its old glory... if it was changed"""
107 if self.__foamVersionChanged:
108
109 environ.update(self.__oldEnvironment)
110
111 - def parse(self,nr=None,exactNr=True):
112 """
113 parse the options
114 @param nr: minimum number of arguments that are to be passed to the application
115 3 is default for pre-1.5 versions of OpenFOAM
116 """
117 (self.options,self.args)=self.parse_args(args=self.argLine)
118
119 if "foamVersion" in dir(self.options):
120 if self.options.foamVersion!=None:
121 if self.options.force32 and self.options.force64:
122 error("A version can't be 32 and 64 bit at the same time")
123
124 self.__foamVersionChanged=True
125 self.__oldEnvironment=deepcopy(environ)
126
127 changeFoamVersion(self.options.foamVersion,
128 force64=self.options.force64,
129 force32=self.options.force32,
130 compileOption=self.options.compileOption,
131 foamCompiler=self.options.foamCompiler,
132 wmCompiler=self.options.wmCompiler)
133 elif self.options.force32 or self.options.force64:
134 warning("Forcing version to be 32 or 64 bit, but no version chosen. Doing nothing")
135 elif self.options.compileOption:
136 warning("No OpenFOAM-version chosen. Can't set compile-option to",self.options.compileOption)
137
138 if nr==None:
139 if oldApp():
140 nr=3
141 else:
142 nr=1
143
144 if len(self.args)<nr:
145 self.error("Too few arguments (%d needed, %d given)" %(nr,len(self.args)))
146
147 maxNr=nr
148 if not oldApp():
149 if "-case" in self.args:
150 maxNr+=2
151
152 if exactNr and len(self.args)>maxNr:
153 self.error("Too many arguments (%d needed, %d given)" %(nr,len(self.args)))
154
155 tmp=self.args
156 self.args=[]
157 for a in tmp:
158 if a.find(" ")>=0 or a.find("(")>=0:
159 a="\""+a+"\""
160 self.args.append(a)
161
163 """Return the arguments left after parsing"""
164 if self.args!=None:
165 return self.args
166 else:
167 return []
168
170 """Return the OpenFOAM-Application to be run"""
171 if self.args!=None:
172 return self.args[0]
173 else:
174 return None
175
177 """Return the options"""
178 if self.options==None:
179 self.error("options have not been parsed yet")
180
181 return self.options
182
192
194 """Go through the lists of options and build a dictionary of keyword
195 arguments (in CamelCase)"""
196 kwArgs={}
197 for og in self.option_groups:
198 for o in og.option_list:
199 raw=o.get_opt_string().strip("-")
200 pos=raw.find("-")
201 if pos<0:
202 name=raw.lower()
203 raw=""
204 else:
205 name=raw[:pos].lower()
206 raw=raw[pos:].strip("-")
207 while len(raw)>0:
208 pos=raw.find("-")
209 if pos<0:
210 name+=raw.capitalize()
211 raw=""
212 else:
213 name+=raw[:pos].capitalize()
214 raw=raw[pos:].strip("-")
215 if not name[0].isalpha() and name[0]!="_":
216 error("Option",o.get_opt_string(),"reduces to",name,
217 "with invalid first character")
218
219 name="".join([c for c in name if c=="_" or c.isalnum])
220 if name in kwArgs:
221 error("Keyword arguement",name,"appears at least twice")
222 kwArgs[name]=o
223
224 return kwArgs
225
227 kwArgs=self._buildKeyordArgumentList()
228 for k,v in iteritems(kw):
229 if k not in kwArgs:
230 raise TypeError("Unknown keyword argument",k,"in",
231 sorted(kwArgs.keys()))
232 o=kwArgs[k]
233 if o.action=="store_true":
234 if type(v)!=bool:
235 raise TypeError("Keyword argument",k,"needs a bool")
236 setattr(self.values,o.dest,v)
237 elif o.action=="store_false":
238 if type(v)!=bool:
239 raise TypeError("Keyword argument",k,"needs a bool")
240 setattr(self.values,o.dest,not v)
241 elif o.action=="store":
242 if o.type:
243 if o.type=="string":
244 if not isinstance(v,string_types) and v!=o.default and o.default!=None:
245 raise TypeError("Keyword argument",k,"must be string or",o.default,". Is a ",type(v))
246 elif o.type in ("int","long"):
247 if not isinstance(v,integer_types):
248 raise TypeError("Keyword argument",k,"must be an integer. Is a ",type(v))
249 elif o.type=="float":
250 if not isinstance(v,integer_types+(float,)):
251 raise TypeError("Keyword argument",k,"must be float. Is a ",type(v))
252 elif o.type=="choice":
253 if v not in o.choices:
254 raise TypeError("Keyword argument",k,"must be one of",o.choices)
255 else:
256 raise RuntimeError("Type",o.type,"not implemented")
257 setattr(self.values,o.dest,v)
258 elif o.action=="append":
259 oldVal=getattr(self.values,o.dest)
260 if type(oldVal) not in (list,tuple):
261 if not type(v) in (list,tuple):
262 raise TypeError("Keyword argument",k,"must be a list or a tuple")
263 setattr(self.values,o.dest,v)
264 else:
265 if type(v) in (list,tuple):
266 setattr(self.values,o.dest,oldVal+v)
267 else:
268 oldVal.append(v)
269 elif o.action=="store_const":
270 setattr(self.values,o.dest,o.const)
271 elif o.action=="append_const":
272 getattr(self.values,o.dest).append(o.const)
273 elif o.action=="count":
274 oldVal=getattr(self.values,o.dest)
275 setattr(self.values,o.dest,oldVal+1)
276 else:
277 raise RuntimeError("Action",o.action,"not implemented")
278
280 """A subcommand of a root command-line application that may be
281 invoked by a SubcommandOptionParser.
282 Taken from https://gist.github.com/sampsyo/462717
283 """
284 - def __init__(self, name, parser=None, help='', aliases=(),nr=None,exactNr=None):
285 """Creates a new subcommand. name is the primary way to invoke
286 the subcommand; aliases are alternate names. parser is an
287 OptionParser responsible for parsing the subcommand's options.
288 help is a short description of the command. If no parser is
289 given, it defaults to a new, empty OptionParser.
290 """
291 self.name = name
292 self.parser = parser or OptionParser()
293 self.aliases = aliases
294 self.help = help
295 self.nr=nr
296 self.exactNr=exactNr
297
299 """Subclass of the regular option parser that allows setting subcommands
300 Inspired by https://gist.github.com/sampsyo/462717
301 """
302
303
304 _HelpSubcommand = Subcommand('help', OptionParser(),
305 help='give detailed help on a specific sub-command',
306 aliases=('?',))
307
308 - def __init__(self,
309 args=None,
310 usage=None,
311 version=None,
312 epilog=None,
313 examples=None,
314 description=None,
315 subcommands=[]):
316 """
317 @param usage: usage string. If missing a default is used
318 @param version: if missing the PyFoam-version is used
319 @param description: description of the utility
320 @param subcommands: list with subcommands to prepopulate the parser
321 @param args: Command line arguments. If unset sys.argv[1:] is used.
322 Can be a string: it will be splitted then unsing the spaces (very primitive), or a list of strings (prefered)
323 """
324 if usage==None:
325 usage="""
326 %prog [general options ...] COMMAND [ARGS ...]
327 %prog help COMMAND"""
328
329 FoamOptionParser.__init__(self,
330 args,
331 usage,
332 version,
333 description=description,
334 epilog=epilog,
335 examples=examples,
336 interspersed=False)
337
338 self.subcommands=subcommands[:]
339 self.addSubcommand(self._HelpSubcommand)
340
341
342 for subcommand in self.subcommands:
343 subcommand.parser.prog = '%s %s' % \
344 (self.get_prog_name(), subcommand.name)
345
346 self.cmdname=None
347 self.__subopts=None
348 self.subargs=None
349
351 if usage==None:
352 cmd.parser.usage=self.usage
353 else:
354 cmd.parser.usage=usage
355 cmd.parser.formatter=TitledHelpFormatter()
356 self.subcommands.append(cmd)
357
358
408
409 - def parse(self,nr=None,exactNr=None):
410 """Do the parsing of a subcommand"""
411 if nr or exactNr:
412 self.error("For calling this implementention no setting of nr and exactNr is valid")
413
414 FoamOptionParser.parse(self,nr=1,exactNr=False)
415
416 if not self.args:
417
418 self.print_help()
419 self.exit()
420 else:
421 cmdname=self.args.pop(0)
422 subcommand = self._subcommand_for_name(cmdname)
423 if not subcommand:
424 self.error('unknown command ' + cmdname)
425
426
427 self.cmdname=subcommand.name
428
429 nr=subcommand.nr
430 exactNr=subcommand.exactNr
431
432 if subcommand is not self._HelpSubcommand:
433 subcommand.parser.usage=subcommand.parser.usage.replace("COMMAND",cmdname)
434
435 self.__subopts,self.subargs=subcommand.parser.parse_args(self.args)
436 if nr!=None:
437 if len(self.subargs)<nr:
438 self.error("Too few arguments for %s (%d needed, %d given)" %(cmdname,nr,len(self.subargs)))
439
440 maxNr=nr
441 if exactNr and len(self.subargs)>maxNr:
442 self.error("Too many arguments for %s (%d needed, %d given)" %(cmdname,nr,len(self.subargs)))
443
444 if subcommand is self._HelpSubcommand:
445 if self.subargs:
446
447 cmdname=self.subargs[0]
448 helpcommand = self._subcommand_for_name(cmdname)
449 if helpcommand!=None:
450 helpcommand.parser.usage=helpcommand.parser.usage.replace("COMMAND",cmdname)
451 helpcommand.parser.print_help()
452 else:
453 self.print_help()
454 self.exit()
455 else:
456
457 self.print_help()
458 self.exit()
459 self.options._update_loose(self.__subopts.__dict__)
460
462 """Return the arguments left after parsing"""
463 if self.subargs!=None:
464 return self.subargs
465 else:
466 return []
467
469 """Return the subcommand in self.subcommands matching the
470 given name. The name may either be the name of a subcommand or
471 an alias. If no subcommand matches, returns None.
472 """
473 for subcommand in self.subcommands:
474 if name == subcommand.name or \
475 name in subcommand.aliases:
476 return subcommand
477
478 return None
479