Salome HOME
fix '&&' in xml launchCommand attribute
[tools/sat.git] / src / options.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 """
20 The Options class that manages the access to all options passed as 
21 parameters in salomeTools command lines
22 """
23
24 import getopt
25 import sys
26 import pprint as PP
27
28 from . import printcolors
29
30 import src
31 import src.debug as DBG # Easy print stderr (for DEBUG only)
32
33 class OptResult(object):
34     """
35     An instance of this class will be the object manipulated
36     in code of all salomeTools commands
37     The aim of this class is to have an elegant syntax to manipulate the options.
38     
39     | Example:        
40     | >> options, remainderArgs = command.parseArguments(args)
41     | >> print(options.output_verbose_level)
42     | >> 'INFO'
43     """
44     def __init__(self):
45         """Initialization
46         """
47         self.__dict__ = dict()
48
49     def __getattr__(self, name):
50         """
51         Overwrite of the __getattr__ function 
52         to customize it for option usage
53         
54         :param name: (str) The attribute to get the value.
55         :return: (str int list boolean level)
56           the value corresponding to the attribute.
57         """
58         if name in self.__dict__:
59             return self.__dict__[name]
60         else:
61             raise AttributeError("--" + name + _(u" is not a valid option"))
62
63     def __setattr__(self, name, value):
64         """
65         Overwrite of the __setattr__ function 
66         to customize it for option usage
67         
68         :param name: (str) The attribute to set.
69         :param value: (str) The value  corresponding to the attribute.
70         :return: None
71         """
72         object.__setattr__(self, name, value)
73
74     def __repr__(self):
75         aStr = PP.pformat(self.__dict__)
76         res = "%s(\n %s\n)" % (self.__class__.__name__, aStr[1:-1])
77         return res
78
79 class Options(object):
80     """
81     Class to manage all salomeTools options
82     """
83     def __init__(self):
84         """Initialization
85         """
86         # The options field stocks all options of a command 
87         # in a list that contains dicts
88         self.options = []
89         # The list of available option type
90         self.availableOptions = "noboolean boolean string int float long list list2 level".split()
91         self.noArgOptions = "noboolean boolean".split()
92         self.default = None
93         self.results = {}
94
95     def add_option(self, shortName, longName, optionType, destName, helpString="", default=None):
96         """
97         Add an option to a command. It gets all attributes
98         of an option and append it in the options field
99         
100         :param shortName: (str) 
101           The short name of the option (as '-l' for level option).
102         :param longName: (str) 
103           The long name of the option (as '--level' for level option).
104         :param optionType: (str) The type of the option (ex "int").
105         :param destName: (str) The name that will be used in the code.
106         :param helpString: (str) 
107           The text to display when user ask for help on a command.     
108         :return: None
109         """
110         tmp = [o['shortName'] for o in self.options if o['shortName'] != '']
111         if shortName in tmp: 
112           raise Exception("option '-%s' existing yet" % shortName)
113         tmp = [o['longName'] for o in self.options if o['longName'] != '']
114         if longName in tmp: 
115           raise Exception("option '--%s' existing yet" % longName)
116
117         option = dict()
118         option['shortName'] = shortName
119         option['longName'] = longName
120
121         if optionType not in self.availableOptions:
122           raise Exception("error optionType '%s' not available." % optionType)
123
124         option['optionType'] = optionType
125         option['destName'] = destName
126         option['helpString'] = helpString
127         option['result'] = default
128         
129         self.options.append(option)
130         
131     def getDetailOption(self, option):
132         """
133         for convenience 
134         
135         :return: (tuple) 4-elements (shortName, longName, optionType, helpString)
136         """
137         oos = option['shortName']
138         ool = option['longName']
139         oot = option['optionType']
140         ooh = option['helpString']
141         return (oos, ool, oot, ooh)
142
143     def get_help(self):
144         """
145         Returns all options stored in self.options 
146         as help message colored string
147         
148         :return: (str) colored string
149         """
150         msg = ""
151         # Do nothing if there are no options
152
153         #there is -h option, always
154         #if len(self.options) == 0:
155         #    return _("No available options.")
156
157         # for all options, gets its values. 
158         # "shortname" is an mandatory field of the options, could be '' 
159         msg += printcolors.printcHeader(_("Available options are:"))
160         for option in self.options:
161             oos, ool, oot, ooh = self.getDetailOption(option)
162             if len(oos) > 0:
163                 msg += "\n -%1s, --%s (%s)\n" % (oos, ool, oot)
164             else:
165                 msg += "\n --%s (%s)\n" % (ool, oot)
166                 
167             msg += "%s\n" % self.indent(ooh, 10)
168         return msg
169
170     def indent(self, text, amount, car=" "):
171         """indent multi lines message"""
172         padding = amount * car
173         return ''.join(padding + line for line in text.splitlines(True))
174                
175     def parse_args(self, argList=None):
176         """
177         Instantiates the class OptResult 
178         that gives access to all options in the code
179         
180         :param argList: (list) the raw list of arguments that were passed
181         :return: (OptResult, list) as (optResult, args) 
182           optResult is the option instance to manipulate in the code. 
183           args is the full raw list of passed options 
184         """
185         # see https://pymotw.com/2/getopt/
186         if argList is None:
187             argList = sys.argv[1:]
188         
189         DBG.write("parse_args", argList)
190         # DBG.write("options", self.options)
191         # format shortNameOption and longNameOption 
192         # to make right arguments to getopt.getopt function
193         shortNameOption = ""
194         longNameOption = []
195         for option in self.options:
196             shortNameOption = shortNameOption + option['shortName']
197             if option['shortName'] != "" and option['optionType'] not in self.noArgOptions:
198                 shortNameOption = shortNameOption + ":"
199
200             if option['longName'] != "":
201                 if option['optionType'] not in self.noArgOptions:
202                     longNameOption.append(option['longName'] + "=")
203                 else:
204                     longNameOption.append(option['longName'])
205
206         # call to getopt.getopt function to get the option 
207         # passed in the command regarding the available options
208         try:
209           optlist, args = getopt.getopt(argList, shortNameOption, longNameOption)
210         except Exception as e:
211           msg = str(e) + " on '%s'\n\n" % " ".join(argList) + self.get_help()
212           raise Exception(msg)
213
214         # instantiate and completing the optResult that will be returned
215         optResult = OptResult()
216         for option in self.options:
217             shortOption = "-" + option['shortName']
218             longOption = "--" + option['longName']
219             optionType = option['optionType']
220             for opt in optlist:
221                 if opt[0] in [shortOption, longOption]:
222                     if optionType == "string":
223                         option['result'] = opt[1]
224                     elif optionType == "boolean":
225                         option['result'] = True
226                     elif optionType == "noboolean":
227                         option['result'] = False
228                     elif optionType == "int":
229                         option['result'] = int(opt[1])
230                     elif optionType == "float":
231                         option['result'] = float(opt[1])
232                     elif optionType == "long":
233                         option['result'] = long(opt[1])
234                     elif optionType == "list":
235                         if option['result'] is None:
236                             option['result'] = list()
237                         option['result'].append(opt[1])
238                     elif optionType == "level": #logger logging levels
239                         option['result'] = self.filterLevel(opt[1])
240                     elif optionType == "list2":
241                         if option['result'] is None:
242                             option['result'] = list()
243                         option['result'] = self.filterList2(opt[1])
244
245             optResult.__setattr__(option['destName'], option['result'])
246             # free the option in order to be able to make 
247             # a new free call of options (API case)
248             option['result'] = None
249
250         self.results = {"optlist": optlist, "optResult": optResult, "args": args, "argList": argList}
251         DBG.write("results", self.results)
252         return optResult, args
253         
254     def filterLevel(self, aLevel):
255       """filter level logging values"""
256       import src.loggingSimple as LOG
257       aLev = aLevel.upper()
258       knownLevels = LOG._knownLevels
259       maxLen = max([len(i) for i in knownLevels])
260       for i in range(maxLen):
261         for lev in knownLevels:
262           if aLev == lev[:i]:
263             DBG.write("filterLevel", "%s -> %s" % (aLevel, lev)) 
264             return lev
265       msg = "Unknown level '%s', accepted are:\n%s" % (aLev, ", ".join(knownLevels))
266       raise Exception(msg)
267       
268     def filterList2(self, aStr):
269       """filter a list as 'KERNEL,YACS,etc.'"""
270       aList = aStr.strip().split(",")
271       # fix list leading ',' as ',KERNEL,...'
272       aList = [i for i in aList if i != ""] # split old list leadin "," as ",KERNEL,ETC..."
273       return aList
274       
275
276     def __repr__(self): 
277         """
278         repr for only self.options and self.results (if present)
279         """
280         aDict = {'options': self.options, 'results': self.results}
281         aStr = PP.pformat(aDict)
282         res = "%s(\n %s\n)" % (self.__class__.__name__, aStr[1:-1])
283         return res
284         
285     def __str__(self): 
286         """
287         str for only resume expected self.options
288         """
289         #aDict = [(k["longName"], k["shortName", k["helpString"]) for k in self.options}
290         #aList = [(k, self.options[k]) for k in sorted(self.options.keys())]
291         aDict = {}
292         for o in self.options:
293           aDict[o["longName"]] = (o["shortName"], o["helpString"])
294         aStr = PP.pformat(aDict)
295         res = "%s(\n %s)" % (self.__class__.__name__, aStr[1:-1])
296         return res
297         
298     def debug_write(self):
299         DBG.write("options and results", self, True)
300
301