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
20 """Wrapper to the usual OptionParser to honor the conventions of OpenFOAM-utilities
21
22 Options that are not used by the script are passed to the OpenFOAM-application"""
23
24 - def __init__(self,
25 args=None,
26 usage=None,
27 version=None,
28 description=None,
29 interspersed=False):
30 """
31 @param usage: usage string. If missing a default is used
32 @param version: if missing the PyFoam-version is used
33 @param description: description of the utility
34 @param interspersed: needs to be false if options should be passed to an OpenFOAM-utility
35 @param args: Command line arguments. If unset sys.argv[1:] is used.
36 Can be a string: it will be splitted then unsing the spaces (very primitive), or a list of strings (prefered)
37 """
38 if usage==None:
39 if oldApp():
40 usage="%prog [options] <foamApplication> <caseDir> <caseName> [foamOptions]"
41 else:
42 usage="%prog [options] <foamApplication> [foamOptions]"
43
44 if version==None:
45 version="%prog "+versionString()
46
47 if args==None:
48 self.argLine=None
49 elif type(args)==str:
50 self.argLine=args.split()
51 else:
52 self.argLine=map(str,args)
53
54 OptionParser.__init__(self,
55 usage=usage,
56
57 version=version,
58 description=description,
59 formatter=TitledHelpFormatter())
60
61 if interspersed:
62 self.enable_interspersed_args()
63 else:
64 self.disable_interspersed_args()
65
66 self.options=None
67 self.args=None
68
69 self.__foamVersionChanged=False
70 self.__oldEnvironment=None
71
73 """Restore the environment to its old glory... if it was changed"""
74 if self.__foamVersionChanged:
75
76 environ.update(self.__oldEnvironment)
77
78 - def parse(self,nr=None,exactNr=True):
79 """
80 parse the options
81 @param nr: minimum number of arguments that are to be passed to the application
82 3 is default for pre-1.5 versions of OpenFOAM
83 """
84 (self.options,self.args)=self.parse_args(args=self.argLine)
85
86 if "foamVersion" in dir(self.options):
87 if self.options.foamVersion!=None:
88 if self.options.force32 and self.options.force64:
89 error("A version can't be 32 and 64 bit at the same time")
90
91 self.__foamVersionChanged=True
92 self.__oldEnvironment=deepcopy(environ)
93
94 changeFoamVersion(self.options.foamVersion,
95 force64=self.options.force64,
96 force32=self.options.force32,
97 compileOption=self.options.compileOption,
98 foamCompiler=self.options.foamCompiler,
99 wmCompiler=self.options.wmCompiler)
100 elif self.options.force32 or self.options.force64:
101 warning("Forcing version to be 32 or 64 bit, but no version chosen. Doing nothing")
102 elif self.options.compileOption:
103 warning("No OpenFOAM-version chosen. Can't set compile-option to",self.options.compileOption)
104
105 if nr==None:
106 if oldApp():
107 nr=3
108 else:
109 nr=1
110
111 if len(self.args)<nr:
112 self.error("Too few arguments (%d needed, %d given)" %(nr,len(self.args)))
113
114 maxNr=nr
115 if not oldApp():
116 if "-case" in self.args:
117 maxNr+=2
118
119 if exactNr and len(self.args)>maxNr:
120 self.error("Too many arguments (%d needed, %d given)" %(nr,len(self.args)))
121
122 tmp=self.args
123 self.args=[]
124 for a in tmp:
125 if a.find(" ")>=0 or a.find("(")>=0:
126 a="\""+a+"\""
127 self.args.append(a)
128
130 """Return the arguments left after parsing"""
131 if self.args!=None:
132 return self.args
133 else:
134 return []
135
137 """Return the OpenFOAM-Application to be run"""
138 if self.args!=None:
139 return self.args[0]
140 else:
141 return None
142
144 """Return the options"""
145 if self.options==None:
146 self.error("options have not been parsed yet")
147
148 return self.options
149
159
161 """Go through the lists of options and build a dictionary of keyword
162 arguments (in CamelCase)"""
163 kwArgs={}
164 for og in self.option_groups:
165 for o in og.option_list:
166 raw=o.get_opt_string().strip("-")
167 pos=raw.find("-")
168 if pos<0:
169 name=raw.lower()
170 raw=""
171 else:
172 name=raw[:pos].lower()
173 raw=raw[pos:].strip("-")
174 while len(raw)>0:
175 pos=raw.find("-")
176 if pos<0:
177 name+=raw.capitalize()
178 raw=""
179 else:
180 name+=raw[:pos].capitalize()
181 raw=raw[pos:].strip("-")
182 if not name[0].isalpha() and name[0]!="_":
183 error("Option",o.get_opt_string(),"reduces to",name,
184 "with invalid first character")
185
186 name="".join([c for c in name if c=="_" or c.isalnum])
187 if name in kwArgs:
188 error("Keyword arguement",name,"appears at least twice")
189 kwArgs[name]=o
190
191 return kwArgs
192
194 kwArgs=self._buildKeyordArgumentList()
195 for k,v in iteritems(kw):
196 if k not in kwArgs:
197 raise TypeError("Unknown keyword argument",k,"in",
198 sorted(kwArgs.keys()))
199 o=kwArgs[k]
200 if o.action=="store_true":
201 if type(v)!=bool:
202 raise TypeError("Keyword argument",k,"needs a bool")
203 setattr(self.values,o.dest,v)
204 elif o.action=="store_false":
205 if type(v)!=bool:
206 raise TypeError("Keyword argument",k,"needs a bool")
207 setattr(self.values,o.dest,not v)
208 elif o.action=="store":
209 if o.type:
210 if o.type=="string":
211 if not isinstance(v,string_types) and v!=o.default and o.default!=None:
212 raise TypeError("Keyword argument",k,"must be string or",o.default,". Is a ",type(v))
213 elif o.type in ("int","long"):
214 if not isinstance(v,integer_types):
215 raise TypeError("Keyword argument",k,"must be an integer. Is a ",type(v))
216 elif o.type=="float":
217 if not isinstance(v,integer_types+(float,)):
218 raise TypeError("Keyword argument",k,"must be float. Is a ",type(v))
219 elif o.type=="choice":
220 if v not in o.choices:
221 raise TypeError("Keyword argument",k,"must be one of",o.choices)
222 else:
223 raise RuntimeError("Type",o.type,"not implemented")
224 setattr(self.values,o.dest,v)
225 elif o.action=="append":
226 oldVal=getattr(self.values,o.dest)
227 if type(oldVal) not in (list,tuple):
228 if not type(v) in (list,tuple):
229 raise TypeError("Keyword argument",k,"must be a list or a tuple")
230 setattr(self.values,o.dest,v)
231 else:
232 if type(v) in (list,tuple):
233 setattr(self.values,o.dest,oldVal+v)
234 else:
235 oldVal.append(v)
236 elif o.action=="store_const":
237 setattr(self.values,o.dest,o.const)
238 elif o.action=="append_const":
239 getattr(self.values,o.dest).append(o.const)
240 elif o.action=="count":
241 oldVal=getattr(self.values,o.dest)
242 setattr(self.values,o.dest,oldVal+1)
243 else:
244 raise RuntimeError("Action",o.action,"not implemented")
245
247 """A subcommand of a root command-line application that may be
248 invoked by a SubcommandOptionParser.
249 Taken from https://gist.github.com/sampsyo/462717
250 """
251 - def __init__(self, name, parser=None, help='', aliases=(),nr=None,exactNr=None):
252 """Creates a new subcommand. name is the primary way to invoke
253 the subcommand; aliases are alternate names. parser is an
254 OptionParser responsible for parsing the subcommand's options.
255 help is a short description of the command. If no parser is
256 given, it defaults to a new, empty OptionParser.
257 """
258 self.name = name
259 self.parser = parser or OptionParser()
260 self.aliases = aliases
261 self.help = help
262 self.nr=nr
263 self.exactNr=exactNr
264
266 """Subclass of the regular option parser that allows setting subcommands
267 Inspired by https://gist.github.com/sampsyo/462717
268 """
269
270
271 _HelpSubcommand = Subcommand('help', OptionParser(),
272 help='give detailed help on a specific sub-command',
273 aliases=('?',))
274
275 - def __init__(self,
276 args=None,
277 usage=None,
278 version=None,
279 description=None,
280 subcommands=[]):
281 """
282 @param usage: usage string. If missing a default is used
283 @param version: if missing the PyFoam-version is used
284 @param description: description of the utility
285 @param subcommands: list with subcommands to prepopulate the parser
286 @param args: Command line arguments. If unset sys.argv[1:] is used.
287 Can be a string: it will be splitted then unsing the spaces (very primitive), or a list of strings (prefered)
288 """
289 if usage==None:
290 usage="""
291 %prog [general options ...] COMMAND [ARGS ...]
292 %prog help COMMAND"""
293
294 FoamOptionParser.__init__(self,
295 args,
296 usage,
297 version,
298 description,
299 interspersed=False)
300
301 self.subcommands=subcommands[:]
302 self.addSubcommand(self._HelpSubcommand)
303
304
305 for subcommand in self.subcommands:
306 subcommand.parser.prog = '%s %s' % \
307 (self.get_prog_name(), subcommand.name)
308
309 self.cmdname=None
310 self.__subopts=None
311 self.subargs=None
312
314 if usage==None:
315 cmd.parser.usage=self.usage
316 else:
317 cmd.parser.usage=usage
318 cmd.parser.formatter=TitledHelpFormatter()
319 self.subcommands.append(cmd)
320
321
371
372 - def parse(self,nr=None,exactNr=None):
373 """Do the parsing of a subcommand"""
374 if nr or exactNr:
375 self.error("For calling this implementention no setting of nr and exactNr is valid")
376
377 FoamOptionParser.parse(self,nr=1,exactNr=False)
378
379 if not self.args:
380
381 self.print_help()
382 self.exit()
383 else:
384 cmdname=self.args.pop(0)
385 subcommand = self._subcommand_for_name(cmdname)
386 if not subcommand:
387 self.error('unknown command ' + cmdname)
388
389
390 self.cmdname=subcommand.name
391
392 nr=subcommand.nr
393 exactNr=subcommand.exactNr
394
395 if subcommand is not self._HelpSubcommand:
396 subcommand.parser.usage=subcommand.parser.usage.replace("COMMAND",cmdname)
397
398 self.__subopts,self.subargs=subcommand.parser.parse_args(self.args)
399 if nr!=None:
400 if len(self.subargs)<nr:
401 self.error("Too few arguments for %s (%d needed, %d given)" %(cmdname,nr,len(self.subargs)))
402
403 maxNr=nr
404 if exactNr and len(self.subargs)>maxNr:
405 self.error("Too many arguments for %s (%d needed, %d given)" %(cmdname,nr,len(self.subargs)))
406
407 if subcommand is self._HelpSubcommand:
408 if self.subargs:
409
410 cmdname=self.subargs[0]
411 helpcommand = self._subcommand_for_name(cmdname)
412 if helpcommand!=None:
413 helpcommand.parser.usage=helpcommand.parser.usage.replace("COMMAND",cmdname)
414 helpcommand.parser.print_help()
415 else:
416 self.print_help()
417 self.exit()
418 else:
419
420 self.print_help()
421 self.exit()
422 self.options._update_loose(self.__subopts.__dict__)
423
425 """Return the arguments left after parsing"""
426 if self.subargs!=None:
427 return self.subargs
428 else:
429 return []
430
432 """Return the subcommand in self.subcommands matching the
433 given name. The name may either be the name of a subcommand or
434 an alias. If no subcommand matches, returns None.
435 """
436 for subcommand in self.subcommands:
437 if name == subcommand.name or \
438 name in subcommand.aliases:
439 return subcommand
440
441 return None
442