Salome HOME
spns #40790: on Debian distributions, use dpkg-query instead of apt to check system...
[tools/sat.git] / src / returnCode.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3
4 #  Copyright (C) 2018-20xx  CEA/DEN
5 #
6 #  This library is free software; you can redistribute it and/or
7 #  modify it under the terms of the GNU Lesser General Public
8 #  License as published by the Free Software Foundation; either
9 #  version 2.1 of the License.
10 #
11 #  This library is distributed in the hope that it will be useful,
12 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 #  Lesser General Public License for more details.
15 #
16 #  You should have received a copy of the GNU Lesser General Public
17 #  License along with this library; if not, write to the Free Software
18 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19
20 """
21 This file contains ReturnCode class
22
23 | Usage:
24 | >> import returnCode as RCO
25 """
26
27 import pprint as PP
28
29 # global module variable
30 _OK_STATUS = "OK"
31 _KO_STATUS = "KO"
32 _NA_STATUS = "NA" # not applicable
33 _UNKNOWN_STATUS = "ND" # not defined
34 _KNOWNFAILURE_STATUS = "KF"
35 _TIMEOUT_STATUS = "TIMEOUT"
36
37 #####################################################
38 class ReturnCode(object):
39   """
40   assume simple return code for methods, with explanation as 'why'.
41   Obviously why is 'why it is not OK', 
42   but also why is 'why it is OK' (if you want). 
43   Optionaly contains a return value as self.getValue()
44   
45   | Usage:
46   | >> import returnCode as RCO
47   | 
48   | >> aValue = doSomethingToReturn()
49   | >> return RCO.ReturnCode("KO", "there is no problem here", aValue)
50   | >> return RCO.ReturnCode("KO", "there is a problem here because etc", None)
51   | >> return RCO.ReturnCode("TIMEOUT_STATUS", "too long here because etc")
52   | >> return RCO.ReturnCode("NA", "not applicable here because etc")
53   | 
54   | >> rc = doSomething()
55   | >> print("short returnCode string", str(rc))
56   | >> print("long returnCode string with value", repr(rc))
57   | 
58   | >> rc1 = RCO.ReturnCode("OK", ...)
59   | >> rc2 = RCO.ReturnCode("KO", ...)
60   | >> rcFinal = rc1 + rc2
61   | >> print("long returnCode string with value", repr(rcFinal)) # KO!
62   | 
63   | >> rc = doSomething()
64   | >> if rc.isOk(): doSomethingAsOK()
65   | >> if not rc.isOk(): doSomethingAsKO()
66   | 
67   | >> rc = doSomething().raiseIfKo() # raise Exception if KO
68   | >> doSomethingWithValue(rc.getValue()) # here i am sure that is OK
69   """
70
71   # redunctant but useful class variables
72   OK_STATUS = _OK_STATUS
73   KO_STATUS = _KO_STATUS
74   NA_STATUS = _NA_STATUS # not applicable
75   UNKNOWN_STATUS = _UNKNOWN_STATUS # not defined
76   KNOWNFAILURE_STATUS = _KNOWNFAILURE_STATUS
77   TIMEOUT_STATUS = _TIMEOUT_STATUS
78
79   # an integer for sys.exit(anInteger)
80   # OKSYS and KOSYS seems equal on linux or windows
81   OKSYS = 0  # OK 
82   KOSYS = 1  # KO
83   NASYS = 2  # KO not applicable return code
84   NDSYS = 3  # KO not defined return code
85   KFSYS = 4  # KO known failure return code
86   TOSYS = 5  # KO time out
87   
88   _TOSYS = { 
89     OK_STATUS: OKSYS,
90     KO_STATUS: KOSYS,
91     NA_STATUS: NASYS,
92     UNKNOWN_STATUS: NDSYS,
93     KNOWNFAILURE_STATUS: KFSYS,
94     TIMEOUT_STATUS: TOSYS, 
95   }
96   _DEFAULT_WHY = "No given explanation"
97   _DEFAULT_VALUE = None
98
99   def __init__(self, status=None, why=None, value=None):
100     self._why = self._DEFAULT_WHY 
101     self._value = self._DEFAULT_VALUE
102     if status is None:
103       self._status = self.UNKNOWN_STATUS
104     else:
105       self.setStatus(status, why, value)
106     
107   def __repr__(self):
108     """complete with value, 'ok, why, value' message"""
109     res = '%s: %s --value: %s' % (self._status, self._why, PP.pformat(self._value))
110     return res
111   
112   def __str__(self):
113     """without value, only simple 'ok, why' message"""
114     res = '%s: %s' % (self._status, self._why)
115     return res
116
117   def indent(self, text, amount=5, ch=' '):
118     """indent multi lines message"""
119     padding = amount * ch
120     res = ''.join(padding + line for line in text.splitlines(True))
121     return res[amount:]
122
123   def __add__(self, rc2):
124     """allows expression 'returnCode1 + returnCode2 + ...' """
125     isOk = self.isOk() and rc2.isOk()
126     newWhy = self._toList(self.getWhy()) + self._toList(rc2.getWhy())
127     newValue = self._toList(self.getValue()) + self._toList(rc2.getValue())    
128     if isOk: 
129       return ReturnCode("OK", newWhy, newValue)
130     else:
131       return ReturnCode("KO", newWhy, newValue)
132     
133   def __radd__(self, other):
134     # see http://www.marinamele.com/2014/04/modifying-add-method-of-python-class.html
135     if other == 0:
136       return self
137     else:
138       return self.__add__(other) 
139     
140   def _toList(self, strOrList):
141     """internal use"""
142     if type(strOrList) is not list: 
143       return [strOrList]
144     else:
145       return strOrList
146
147   def toSys(self):
148     """return system return code as bash or bat"""
149     try:
150       return self._TOSYS[self._status]
151     except:
152       return self._TOSYS[self.NA_STATUS]
153
154   def toXmlPassed(self):
155     """return xml  return code as '0' (passed) or '1' (not passed)"""
156     if self.isOk(): 
157       return "0"
158     else:
159       return "1"
160     
161   def getWhy(self):
162     """return why as str or list if sum or some ReturnCode"""
163     return self._why
164     
165   def setWhy(self, why):
166     self._why = why
167     
168   def getValue(self):
169     return self._value
170     
171   def setValue(self, value):
172     """choice as not deep copying if mutables value"""
173     # TODO deepcopy maybe for value, not yet
174     self._value = value
175     
176   def setStatus(self, status, why=None, value=None):
177     if why is None: 
178       aWhy = self._DEFAULT_WHY
179     else:
180       aWhy = why
181       
182     if status in self._TOSYS.keys():
183       self._status = status
184       self._why = aWhy
185     else:
186       self._status = self.NA_STATUS
187       self._why = "Error status '%s' for '%s'" % (status, aWhy)
188       
189     if value is not None:
190       # TODO deepcopy maybe for value, not yet
191       self._value = value
192     else:
193       self._value = self._DEFAULT_VALUE
194       
195   def getStatus(self):
196     return self._status
197
198   def isOk(self):
199     """
200     return True if ok.
201     inexisting method isKo(), use more explicit/readability 'if not res.isOk()'
202     """
203     return (self._status == self.OK_STATUS)
204   
205   def raiseIfKo(self):
206     """
207     raise an exception with message why if not ok, else return self.
208     This trick is to write usage
209     
210     | Usage:
211     | >> rc = doSomething().raiseIfKo() # raise Exception if KO
212     | >> doSomethingWithValue(rc.getValue()) # here i am sure that is OK
213     """
214     if self.isOk(): 
215       return self
216     else:
217       raise Exception(self.getWhy())
218
219 def ReturnCodeFromList(aListOfReturnCodes):
220   """
221   Create ReturnCode from list of ReturnCode
222   
223   convenience over "+" operand
224   """
225   res = "OK"
226   whyes = []
227   for rc in aListOfReturnCodes:
228     if not rc.isOk():
229       res = "KO"
230     whyes.append(str(rc))
231   reswhy = "\n  ".join(whyes)
232   return ReturnCode(res, "\n  " + reswhy)
233     
234