Salome HOME
print stack for everybody, activate debug mode when sat -g option is used
[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
59 # Compatibility python 2/3 for unicode
60 try:
61     _test = unicode
62 except:
63     unicode = str
64
65 # Compatibility python 2/3 for StringIO
66 try:
67     from StringIO import StringIO
68 except ImportError:
69     from io import StringIO
70
71 _debug = [False] #support push/pop for temporary activate debug outputs
72
73 _user = os.environ['USER']
74 # wambeke is christian at home
75 _developers = ["christian", "wambeke"] # crouzet, kloss ...
76
77
78 def isDeveloper():
79     """if you are a developer, sometimes you want verbose traces etc."""
80     res = _user in _developers
81     return res
82
83 def indent(text, amount=2, ch=' '):
84     """indent multi lines message"""
85     padding = amount * ch
86     return ''.join(padding + line for line in text.splitlines(True))
87
88 def isTypeConfig(var):
89     """To know if var is instance from Config/pyconf"""
90     typ = str(type(var))
91     # print "isTypeConfig" ,type, dir(var)
92     if ".pyconf.Config" in typ: return True
93     if ".pyconf.Mapping" in typ: return True
94     if ".pyconf.Sequence" in typ: return True
95     # print "NOT isTypeConfig %s" % typ
96     return False
97     
98 def write(title, var="", force=None, fmt="\n#### DEBUG: %s:\n%s\n"):
99     """write sys.stderr a message if _debug[-1]==True or optionaly force=True"""
100     if _debug[-1] or force:
101       tvar = type(var)
102       typ = str(tvar)
103       if isTypeConfig(var):
104         sys.stderr.write(fmt % (title, indent(getStrConfigDbg(var))))
105         return
106       if 'UnittestStream' in typ:
107         sys.stderr.write(fmt % (title, indent(var.getLogs())))
108         return  
109       if tvar is not str and tvar is not unicode:
110         sys.stderr.write(fmt % (title, indent(PP.pformat(var))))
111         return
112       sys.stderr.write(fmt % (title, indent(var)))
113       return
114     return
115
116 def tofix(title, var="", force=None):
117     """
118     write sys.stderr a message if _debug[-1]==True or optionaly force=True
119     use this only if no logger accessible for classic logger.warning(message)
120     """
121     fmt = "\n#### TOFIX: %s:\n%s\n"
122     write(title, var, force, fmt)
123
124 def push_debug(aBool):
125     """set debug outputs activated, or not"""
126     _debug.append(aBool)
127
128 def pop_debug():
129     """restore previous debug outputs status"""
130     if len(_debug) > 1:
131         return _debug.pop()
132     else:
133         sys.stderr.write("\nERROR: pop_debug: too much pop.")
134         return None
135
136
137 def format_exception(msg, limit=None, trace=None):
138   """
139   Format a stack trace and the exception information.
140   as traceback.format_exception(), without color
141   with traceback only if (_debug) or (DBG._user in DBG._developers)
142   """
143   etype, value, tb = sys.exc_info()
144   res = msg
145   if tb:
146     res += "\nTraceback (most recent call last):\n"
147     res += "".join(traceback.format_tb(tb, limit))  # [:-1])
148   res += "\n"
149   res += "\n".join(traceback.format_exception_only(etype, value))
150   return res
151
152 def format_color_exception(msg, limit=None, trace=None):
153   """
154   Format a stack trace and the exception information.
155   as traceback.format_exception(), with color
156   with traceback only if _debug or isDeveloper())
157   """
158   etype, value, tb = sys.exc_info()
159   if _debug[-1] or isDeveloper():
160     res = "<red>" + msg
161     if tb:
162       res += "<yellow>\nTraceback (most recent call last):\n"
163       res += "".join(traceback.format_tb(tb, limit))  # [:-1])
164     res += "\n<red>"
165     res += "\n".join(traceback.format_exception_only(etype, value))
166     return res + "<reset>"
167   else:
168     res = "<red>" + msg  # + "<bright>"
169     res += "".join(traceback.format_exception_only(etype, value))
170     return res + "<reset>"
171
172
173 ###############################################
174 # utilitaires divers pour debug
175 ###############################################
176
177 class OutStream(StringIO):
178     """
179     utility class for pyconf.Config output iostream
180     """
181     def close(self):
182       """
183       because Config.__save__ calls close() stream as file
184       keep value before lost as self.value
185       """
186       self.value = self.getvalue()
187       StringIO.close(self)
188     
189 class InStream(StringIO):
190     """utility class for pyconf.Config input iostream"""
191     pass
192
193 def getLocalEnv():
194     """get string for environment variables representation"""
195     res = ""
196     for i in sorted(os.environ):
197         res += "%s : %s\n" % (i, os.environ[i])
198     return res
199
200 # save as initial Config.save() moved as Config.__save__() 
201 def saveConfigStd(config, aStream):
202     """returns as file .pyconf"""
203     indent =  0
204     config.__save__(aStream, indent) 
205
206 def getStrConfigStd(config):
207     """set string as saveConfigStd, as file .pyconf"""
208     outStream = OutStream()
209     saveConfigStd(config, outStream)
210     return outStream.value
211
212 def getStrConfigDbg(config):
213     """
214     set string as saveConfigDbg, 
215     as (path expression evaluation) for debug
216     """
217     outStream = OutStream()
218     saveConfigDbg(config, outStream)
219     return outStream.value
220
221 def saveConfigDbg(config, aStream, indent=0, path=""):
222     """pyconf returns multilines (path expression evaluation) for debug"""
223     _saveConfigRecursiveDbg(config, aStream, indent, path, 0)
224     aStream.close() # as config.__save__()
225
226 def _saveConfigRecursiveDbg(config, aStream, indent, path, nb):
227     """pyconf inspired from Mapping.__save__"""
228     debug = False
229     nbp = nb + 1 # depth recursive
230     if indent <= 0: 
231       indentp = 0
232     else:
233       indentp = indent + 2
234       
235     if nbp > 10: # protection
236       # raise Exception("!!! ERROR: Circular reference after %s" % aStream.getvalue())
237       # raise Exception("!!! ERROR: Circular reference %s" % path)
238       aStream.write("<red>!!! ERROR: Circular reference after %s<reset>\n" % path)
239       return
240     
241     indstr = indent * ' ' # '':no indent, ' ':indent
242     strType = str(type(config))
243     if debug: print("saveDbg Type %s %s" % (path, strType))
244     
245     if "Sequence" in strType:
246       for i in range(len(config)):
247         _saveConfigRecursiveDbg(config[i], aStream, indentp, path+"[%i]" % i, nbp)
248       return
249     '''
250     if "Reference" in strType:
251       try:
252         #evaluate = value.resolve(config)
253         aStream.write("<header>%s%s<reset> : %s <yellow>--> '%s'<reset>\n" % (indstr, path, config, str(config)))
254       except Exception as e:  
255         aStream.write("<header>%s%s<reset> : <red>!!! ERROR: %s !!!<reset>\n" % (indstr, path, e.message))     
256       return
257     '''
258     
259     try: #type config, mapping
260       order = object.__getattribute__(config, 'order')
261       data = object.__getattribute__(config, 'data')
262     except:
263       aStream.write("%s%s : '%s'\n" % (indstr, path, str(config)))
264       return     
265     for key in sorted(data): #order): # data as sort alphabetical, order as initial order
266       value = data[key]
267       strType = str(type(value))
268       if debug: print('strType %s %s %s' % (path, key, strType))
269       if "Config" in strType:
270         _saveConfigRecursiveDbg(value, aStream, indentp, path+"."+key, nbp)
271         continue
272       if "Mapping" in strType:
273         _saveConfigRecursiveDbg(value, aStream, indentp, path+"."+key, nbp)
274         continue
275       if "Sequence" in strType:
276         for i in range(len(value)):
277           _saveConfigRecursiveDbg(value.data[i], aStream, indentp, path+"."+key+"[%i]" % i, nbp)
278         continue
279       if "Expression" in strType:
280         try:
281           evaluate = value.evaluate(config)
282           aStream.write("%s%s.%s : %s --> '%s'\n" % (indstr, path, key, str(value), evaluate))
283         except Exception as e:      
284           aStream.write("%s%s.%s : !!! ERROR: %s !!!\n" % (indstr, path, key, e.message))     
285         continue
286       if "Reference" in strType:
287         try:
288           evaluate = value.resolve(config)
289           aStream.write("%s%s.%s : %s --> '%s'\n" % (indstr, path, key, str(value), evaluate))
290         except Exception as e:  
291           aStream.write("%s%s.%s : !!! ERROR: %s !!!\n" % (indstr, path, key, e.message))     
292         continue
293       if type(value) in [str, bool, int, type(None), unicode]:
294         aStream.write("%s%s.%s : '%s'\n" % (indstr, path, key, str(value)))
295         continue
296       try:
297         aStream.write("!!! TODO fix that %s %s%s.%s : %s\n" % (type(value), indstr, path, key, str(value)))
298       except Exception as e:      
299         aStream.write("%s%s.%s : !!! %s\n" % (indstr, path, key, e.message))