Salome HOME
f4674b56be763e2c4ef39ed00697c96c157429a3
[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         if len(self.options) == 0:
153             return _("No available options.")
154
155         # for all options, gets its values. 
156         # "shortname" is an mandatory field of the options, could be '' 
157         msg += printcolors.printcHeader(_("Available options are:"))
158         for option in self.options:
159             oos, ool, oot, ooh = self.getDetailOption(option)
160             if len(oos) > 0:
161                 msg += "\n -%1s, --%s (%s)\n" % (oos, ool, oot)
162             else:
163                 msg += "\n --%s (%s)\n" % (ool, oot)
164                 
165             msg += "%s\n" % self.indent(ooh, 10)
166         return msg
167
168     def print_help(self):
169         """
170         Method that display all options stored in self.options and there help
171         
172         :return: None
173         """
174         print(self.get_help())
175         return
176
177     def indent(self, text, amount, car=" "):
178         """indent multi lines message"""
179         padding = amount * car
180         return ''.join(padding + line for line in text.splitlines(True))
181                
182     def parse_args(self, argList=None):
183         """
184         Instantiates the class OptResult 
185         that gives access to all options in the code
186         
187         :param argList: (list) the raw list of arguments that were passed
188         :return: (OptResult, list) as (optResult, args) 
189           optResult is the option instance to manipulate in the code. 
190           args is the full raw list of passed options 
191         """
192         # see https://pymotw.com/2/getopt/
193         if argList is None:
194             argList = sys.argv[1:]
195         
196         DBG.write("parse_args", argList)
197         # DBG.write("options", self.options)
198         # format shortNameOption and longNameOption 
199         # to make right arguments to getopt.getopt function
200         shortNameOption = ""
201         longNameOption = []
202         for option in self.options:
203             shortNameOption = shortNameOption + option['shortName']
204             if option['shortName'] != "" and option['optionType'] not in self.noArgOptions:
205                 shortNameOption = shortNameOption + ":"
206
207             if option['longName'] != "":
208                 if option['optionType'] not in self.noArgOptions:
209                     longNameOption.append(option['longName'] + "=")
210                 else:
211                     longNameOption.append(option['longName'])
212
213         # call to getopt.getopt function to get the option 
214         # passed in the command regarding the available options
215         try:
216           optlist, args = getopt.getopt(argList, shortNameOption, longNameOption)
217         except Exception as e:
218           msg = str(e) + " on '%s'\n\n" % " ".join(argList) + self.get_help()
219           raise Exception(msg)
220
221         # instantiate and completing the optResult that will be returned
222         optResult = OptResult()
223         for option in self.options:
224             shortOption = "-" + option['shortName']
225             longOption = "--" + option['longName']
226             optionType = option['optionType']
227             for opt in optlist:
228                 if opt[0] in [shortOption, longOption]:
229                     if optionType == "string":
230                         option['result'] = opt[1]
231                     elif optionType == "boolean":
232                         option['result'] = True
233                     elif optionType == "noboolean":
234                         option['result'] = False
235                     elif optionType == "int":
236                         option['result'] = int(opt[1])
237                     elif optionType == "float":
238                         option['result'] = float(opt[1])
239                     elif optionType == "long":
240                         option['result'] = long(opt[1])
241                     elif optionType == "list":
242                         if option['result'] is None:
243                             option['result'] = list()
244                         option['result'].append(opt[1])
245                     elif optionType == "level": #logger logging levels
246                         option['result'] = self.filterLevel(opt[1])
247                     elif optionType == "list2":
248                         if option['result'] is None:
249                             option['result'] = list()
250                         option['result'] = self.filterList2(opt[1])
251
252             optResult.__setattr__(option['destName'], option['result'])
253             # free the option in order to be able to make 
254             # a new free call of options (API case)
255             option['result'] = None
256
257         self.results = {"optlist": optlist, "optResult": optResult, "args": args, "argList": argList}
258         DBG.write("results", self.results)
259         return optResult, args
260         
261     def filterLevel(self, aLevel):
262       """filter level logging values"""
263       import src.loggingSat as LOG
264       aLev = aLevel.upper()
265       knownLevels = LOG._knownLevels
266       maxLen = max([len(i) for i in knownLevels])
267       for i in range(maxLen):
268         for lev in knownLevels:
269           if aLev == lev[:i]:
270             DBG.write("filterLevel", "%s -> %s" % (aLevel, lev)) 
271             return lev
272       msg = "Unknown level '%s', accepted are:\n%s" % (aLev, ", ".join(knownLevels))
273       raise Exception(msg)
274       
275     def filterList2(self, aStr):
276       """filter a list as 'KERNEL,YACS,etc.'"""
277       aList = aStr.strip().split(",")
278       return aList
279       
280
281     def __repr__(self): 
282         """
283         repr for only self.options and self.results (if present)
284         """
285         aDict = {'options': self.options, 'results': self.results}
286         aStr = PP.pformat(aDict)
287         res = "%s(\n %s\n)" % (self.__class__.__name__, aStr[1:-1])
288         return res
289         
290     def __str__(self): 
291         """
292         str for only resume expected self.options
293         """
294         #aDict = [(k["longName"], k["shortName", k["helpString"]) for k in self.options}
295         #aList = [(k, self.options[k]) for k in sorted(self.options.keys())]
296         aDict = {}
297         for o in self.options:
298           aDict[o["longName"]] = (o["shortName"], o["helpString"])
299         aStr = PP.pformat(aDict)
300         res = "%s(\n %s)" % (self.__class__.__name__, aStr[1:-1])
301         return res
302         
303     def debug_write(self):
304         DBG.write("options and results", self, True)
305
306