Salome HOME
style: black format
[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
220 def ReturnCodeFromList(aListOfReturnCodes):
221     """
222     Create ReturnCode from list of ReturnCode
223
224     convenience over "+" operand
225     """
226     res = "OK"
227     whyes = []
228     for rc in aListOfReturnCodes:
229         if not rc.isOk():
230             res = "KO"
231         whyes.append(str(rc))
232     reswhy = "\n  ".join(whyes)
233     return ReturnCode(res, "\n  " + reswhy)