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