Salome HOME
style: black format
[tools/sat.git] / src / debug.py
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 #  Copyright (C) 2010-2018  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 assume DEBUG functionalities use.
22 Print salomeTools debug messages in sys.stderr.
23 Show pretty print debug representation from instances of SAT classes 
24 (pretty print src.pyconf.Config)
25
26 | Warning: supposedly show messages in SAT development phase, not production
27
28 | Usage:
29 | >> import debug as DBG
30 | >> DBG.write("aTitle", aVariable)        # not shown in production 
31 | >> DBG.write("aTitle", aVariable, True)  # unconditionaly shown (as show=True)
32
33 | to set show message as development phase:
34 | >> DBG.push_debug(True)
35
36 | to set no show message as production phase:
37 | >> DBG.push_debug(False)
38
39 | to set show message temporary as development phase, only in a method:
40 | >> def aMethodToDebug(...):
41 | >>   DBG.push_debug(True)              #force show as appended status
42 | >>   etc. method code with some DBG.write()
43 | >>   DBG.pop_debug()                   #restore previous status (show or not show)
44 | >>   return
45
46 | to set a message for future fix, as temporary problem to not forget:
47 | DBG.tofix("aTitle", aVariable, True/False) #True/False in production shown, or not
48
49 | in command line interface you could redirect stderr to file 'myDebug.log':
50 | >> sat compile ... 2> myDebug.log   # only stderr
51 | >> sat compile ... &> myDebug.log   # stdout and stderr
52 """
53
54 import os
55 import sys
56 import traceback
57 import pprint as PP
58 import inspect
59 import src
60
61 # Compatibility python 2/3 for unicode
62 try:
63     _test = unicode
64 except:
65     unicode = str
66
67 # Compatibility python 2/3 for StringIO
68 try:
69     from StringIO import StringIO
70 except ImportError:
71     from io import StringIO
72
73 _debug = [False]  # support push/pop for temporary activate debug outputs
74
75
76 def isDeveloper():
77     """
78     if you are a developer, sometimes you want verbose traces unconditionally
79     export SAT_DEVELOPER_MODE=1 before launch sat command to do that
80     """
81     res = os.getenv("SAT_DEVELOPER_MODE", "0")
82     if res in "YES yes 1 Y y".split():
83         return True
84     else:
85         return False
86
87
88 def indent(text, amount=2, ch=" "):
89     """indent multi lines message"""
90     padding = amount * ch
91     return "".join(padding + line for line in text.splitlines(True))
92
93
94 def isTypeConfig(var):
95     """To know if var is instance from Config/pyconf"""
96     typ = str(type(var))
97     # print "isTypeConfig" ,type, dir(var)
98     if ".pyconf.Config" in typ:
99         return True
100     if ".pyconf.Mapping" in typ:
101         return True
102     if ".pyconf.Sequence" in typ:
103         return True
104     # print "NOT isTypeConfig %s" % typ
105     return False
106
107
108 def write(title, var="", force=None, fmt="  %s:\n%s\n####\n"):
109     """write sys.stderr a message if _debug[-1]==True or optionaly force=True"""
110     if _debug[-1] or force:
111         callerframerecord = inspect.stack()[1]  # get info of the caller
112         frame = callerframerecord[0]
113         info = inspect.getframeinfo(frame)
114         sys.stderr.write(
115             "\n#### DEBUG - %s:%s (%s) ####\n"
116             % (info.filename, info.lineno, info.function)
117         )
118         tvar = type(var)
119         typ = str(tvar)
120         if isTypeConfig(var):
121             sys.stderr.write(fmt % (title, indent(getStrConfigDbg(var))))
122             return
123         if "UnittestStream" in typ:
124             sys.stderr.write(fmt % (title, indent(var.getLogs())))
125             return
126         if tvar is not str and tvar is not unicode:
127             sys.stderr.write(fmt % (title, indent(PP.pformat(var))))
128             return
129         sys.stderr.write(fmt % (title, indent(var)))
130         return
131     return
132
133
134 def tofix(title, var="", force=None):
135     """
136     write sys.stderr a message if _debug[-1]==True or optionaly force=True
137     use this only if no logger accessible for classic logger.warning(message)
138     """
139     if _debug[-1] or isDeveloper():
140         callerframerecord = inspect.stack()[1]  # get info of the caller
141         frame = callerframerecord[0]
142         info = inspect.getframeinfo(frame)
143         fmt = (
144             "#### TOFIX - "
145             + str(info.filename)
146             + ":"
147             + str(info.lineno)
148             + " ("
149             + str(info.function)
150             + ") ####\n   %s:\n%s\n"
151         )
152         write(title, var, force, fmt)
153
154
155 def push_debug(aBool):
156     """set debug outputs activated, or not"""
157     _debug.append(aBool)
158
159
160 def pop_debug():
161     """restore previous debug outputs status"""
162     if len(_debug) > 1:
163         return _debug.pop()
164     else:
165         sys.stderr.write("\nERROR: pop_debug: too much pop.")
166         return None
167
168
169 def format_exception(msg, limit=None, trace=None):
170     """
171     Format a stack trace and the exception information.
172     as traceback.format_exception(), without color
173     with traceback only if (_debug) or (DBG.isDeveloper())
174     """
175     etype, value, tb = sys.exc_info()
176     res = msg
177     if tb:
178         res += "\nTraceback (most recent call last):\n"
179         res += "".join(traceback.format_tb(tb, limit))  # [:-1])
180     res += "\n"
181     res += "\n".join(traceback.format_exception_only(etype, value))
182     return res
183
184
185 def format_color_exception(msg, limit=None, trace=None):
186     """
187     Format a stack trace and the exception information.
188     as traceback.format_exception(), with color
189     with traceback only if _debug or isDeveloper())
190     """
191     etype, value, tb = sys.exc_info()
192     if _debug[-1] or isDeveloper():
193         res = "<red>" + msg
194         if tb:
195             res += "<yellow>\nTraceback (most recent call last):\n"
196             res += "".join(traceback.format_tb(tb, limit))  # [:-1])
197         res += "\n<red>"
198         res += "\n".join(traceback.format_exception_only(etype, value))
199         return res + "<reset>"
200     else:
201         res = "<red>" + msg  # + "<bright>"
202         res += "".join(traceback.format_exception_only(etype, value))
203         return res + "<reset>"
204
205
206 ###############################################
207 # utilitaires divers pour debug
208 ###############################################
209
210
211 class OutStream(StringIO):
212     """
213     utility class for pyconf.Config output iostream
214     """
215
216     def close(self):
217         """
218         because Config.__save__ calls close() stream as file
219         keep value before lost as self.value
220         """
221         self.value = self.getvalue()
222         StringIO.close(self)
223
224
225 class InStream(StringIO):
226     """utility class for pyconf.Config input iostream"""
227
228     pass
229
230
231 def getLocalEnv():
232     """get string for environment variables representation"""
233     res = ""
234     for i in sorted(os.environ):
235         res += "%s : %s\n" % (i, os.environ[i])
236     return res
237
238
239 # save as initial Config.save() moved as Config.__save__()
240 def saveConfigStd(config, aStream):
241     """returns as file .pyconf"""
242     indent = 0
243     config.__save__(aStream, indent)
244
245
246 def getStrConfigStd(config):
247     """set string as saveConfigStd, as file .pyconf"""
248     outStream = OutStream()
249     saveConfigStd(config, outStream)
250     return outStream.value
251
252
253 def getStrConfigDbg(config):
254     """
255     set string as saveConfigDbg,
256     as (path expression evaluation) for debug
257     """
258     outStream = OutStream()
259     saveConfigDbg(config, outStream)
260     return outStream.value
261
262
263 def saveConfigDbg(config, aStream, indent=0, path=""):
264     """pyconf returns multilines (path expression evaluation) for debug"""
265     _saveConfigRecursiveDbg(config, aStream, indent, path, 0)
266     aStream.close()  # as config.__save__()
267
268
269 def _saveConfigRecursiveDbg(config, aStream, indent, path, nb):
270     """pyconf inspired from Mapping.__save__"""
271     debug = False
272     nbp = nb + 1  # depth recursive
273     if indent <= 0:
274         indentp = 0
275     else:
276         indentp = indent + 2
277
278     if nbp > 10:  # protection
279         # raise Exception("!!! ERROR: Circular reference after %s" % aStream.getvalue())
280         # raise Exception("!!! ERROR: Circular reference %s" % path)
281         aStream.write("<red>!!! ERROR: Circular reference after %s<reset>\n" % path)
282         return
283
284     indstr = indent * " "  # '':no indent, ' ':indent
285     strType = str(type(config))
286     if debug:
287         print("saveDbg Type %s %s" % (path, strType))
288
289     if "Sequence" in strType:
290         for i in range(len(config)):
291             _saveConfigRecursiveDbg(config[i], aStream, indentp, path + "[%i]" % i, nbp)
292         return
293     """
294     if "Reference" in strType:
295       try:
296         #evaluate = value.resolve(config)
297         aStream.write("<header>%s%s<reset> : %s <yellow>--> '%s'<reset>\n" % (indstr, path, config, str(config)))
298       except Exception as e:  
299         aStream.write("<header>%s%s<reset> : <red>!!! ERROR: %s !!!<reset>\n" % (indstr, path, str(e)))     
300       return
301     """
302
303     try:  # type config, mapping
304         order = object.__getattribute__(config, "order")
305         data = object.__getattribute__(config, "data")
306     except:
307         aStream.write("%s%s : '%s'\n" % (indstr, path, str(config)))
308         return
309     for key in sorted(
310         data
311     ):  # order): # data as sort alphabetical, order as initial order
312         value = data[key]
313         strType = str(type(value))
314         if debug:
315             print("strType %s %s %s" % (path, key, strType))
316         if "Config" in strType:
317             _saveConfigRecursiveDbg(value, aStream, indentp, path + "." + key, nbp)
318             continue
319         if "Mapping" in strType:
320             _saveConfigRecursiveDbg(value, aStream, indentp, path + "." + key, nbp)
321             continue
322         if "Sequence" in strType:
323             for i in range(len(value)):
324                 _saveConfigRecursiveDbg(
325                     value.data[i], aStream, indentp, path + "." + key + "[%i]" % i, nbp
326                 )
327             continue
328         if "Expression" in strType:
329             try:
330                 evaluate = value.evaluate(config)
331                 aStream.write(
332                     "%s%s.%s : %s --> '%s'\n"
333                     % (indstr, path, key, str(value), evaluate)
334                 )
335             except Exception as e:
336                 aStream.write(
337                     "%s%s.%s : !!! ERROR: %s !!!\n" % (indstr, path, key, str(e))
338                 )
339             continue
340         if "Reference" in strType:
341             try:
342                 evaluate = value.resolve(config)
343                 aStream.write(
344                     "%s%s.%s : %s --> '%s'\n"
345                     % (indstr, path, key, str(value), evaluate)
346                 )
347             except Exception as e:
348                 aStream.write(
349                     "%s%s.%s : !!! ERROR: %s !!!\n" % (indstr, path, key, str(e))
350                 )
351             continue
352         if type(value) in [str, bool, int, type(None), unicode]:
353             aStream.write("%s%s.%s : '%s'\n" % (indstr, path, key, str(value)))
354             continue
355         try:
356             aStream.write(
357                 "!!! TODO fix that %s %s%s.%s : %s\n"
358                 % (type(value), indstr, path, key, str(value))
359             )
360         except Exception as e:
361             aStream.write("%s%s.%s : !!! %s\n" % (indstr, path, key, str(e)))