Salome HOME
4d2eb0207685c78f88d4936b33fbd89ff9ea9193
[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"] # 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   if _debug[-1] or isDeveloper():
145     res = msg
146     if tb:
147       res += "\nTraceback (most recent call last):\n"
148       res += "".join(traceback.format_tb(tb, limit))  # [:-1])
149     res += "\n"
150     res += "\n".join(traceback.format_exception_only(etype, value))
151     return res
152   else:
153     res = msg
154     res += "".join(traceback.format_exception_only(etype, value))
155     return res
156
157 def format_color_exception(msg, limit=None, trace=None):
158   """
159   Format a stack trace and the exception information.
160   as traceback.format_exception(), with color
161   with traceback only if _debug or isDeveloper())
162   """
163   etype, value, tb = sys.exc_info()
164   if _debug[-1] or isDeveloper():
165     res = "<red>" + msg
166     if tb:
167       res += "<yellow>\nTraceback (most recent call last):\n"
168       res += "".join(traceback.format_tb(tb, limit))  # [:-1])
169     res += "\n<red>"
170     res += "\n".join(traceback.format_exception_only(etype, value))
171     return res + "<reset>"
172   else:
173     res = "<red>" + msg  # + "<bright>"
174     res += "".join(traceback.format_exception_only(etype, value))
175     return res + "<reset>"
176
177
178 ###############################################
179 # utilitaires divers pour debug
180 ###############################################
181
182 class OutStream(StringIO):
183     """
184     utility class for pyconf.Config output iostream
185     """
186     def close(self):
187       """
188       because Config.__save__ calls close() stream as file
189       keep value before lost as self.value
190       """
191       self.value = self.getvalue()
192       StringIO.close(self)
193     
194 class InStream(StringIO):
195     """utility class for pyconf.Config input iostream"""
196     pass
197
198 def getLocalEnv():
199     """get string for environment variables representation"""
200     res = ""
201     for i in sorted(os.environ):
202         res += "%s : %s\n" % (i, os.environ[i])
203     return res
204
205 # save as initial Config.save() moved as Config.__save__() 
206 def saveConfigStd(config, aStream):
207     """returns as file .pyconf"""
208     indent =  0
209     config.__save__(aStream, indent) 
210
211 def getStrConfigStd(config):
212     """set string as saveConfigStd, as file .pyconf"""
213     outStream = OutStream()
214     saveConfigStd(config, outStream)
215     return outStream.value
216
217 def getStrConfigDbg(config):
218     """
219     set string as saveConfigDbg, 
220     as (path expression evaluation) for debug
221     """
222     outStream = OutStream()
223     saveConfigDbg(config, outStream)
224     return outStream.value
225
226 def saveConfigDbg(config, aStream, indent=0, path=""):
227     """pyconf returns multilines (path expression evaluation) for debug"""
228     _saveConfigRecursiveDbg(config, aStream, indent, path, 0)
229     aStream.close() # as config.__save__()
230
231 def _saveConfigRecursiveDbg(config, aStream, indent, path, nb):
232     """pyconf inspired from Mapping.__save__"""
233     debug = False
234     nbp = nb + 1 # depth recursive
235     if indent <= 0: 
236       indentp = 0
237     else:
238       indentp = indent + 2
239       
240     if nbp > 10: # protection
241       # raise Exception("!!! ERROR: Circular reference after %s" % aStream.getvalue())
242       # raise Exception("!!! ERROR: Circular reference %s" % path)
243       aStream.write("<red>!!! ERROR: Circular reference after %s<reset>\n" % path)
244       return
245     
246     indstr = indent * ' ' # '':no indent, ' ':indent
247     strType = str(type(config))
248     if debug: print("saveDbg Type %s %s" % (path, strType))
249     
250     if "Sequence" in strType:
251       for i in range(len(config)):
252         _saveConfigRecursiveDbg(config[i], aStream, indentp, path+"[%i]" % i, nbp)
253       return
254     '''
255     if "Reference" in strType:
256       try:
257         #evaluate = value.resolve(config)
258         aStream.write("<header>%s%s<reset> : %s <yellow>--> '%s'<reset>\n" % (indstr, path, config, str(config)))
259       except Exception as e:  
260         aStream.write("<header>%s%s<reset> : <red>!!! ERROR: %s !!!<reset>\n" % (indstr, path, e.message))     
261       return
262     '''
263     
264     try: #type config, mapping
265       order = object.__getattribute__(config, 'order')
266       data = object.__getattribute__(config, 'data')
267     except:
268       aStream.write("%s%s : '%s'\n" % (indstr, path, str(config)))
269       return     
270     for key in sorted(data): #order): # data as sort alphabetical, order as initial order
271       value = data[key]
272       strType = str(type(value))
273       if debug: print('strType %s %s %s' % (path, key, strType))
274       if "Config" in strType:
275         _saveConfigRecursiveDbg(value, aStream, indentp, path+"."+key, nbp)
276         continue
277       if "Mapping" in strType:
278         _saveConfigRecursiveDbg(value, aStream, indentp, path+"."+key, nbp)
279         continue
280       if "Sequence" in strType:
281         for i in range(len(value)):
282           _saveConfigRecursiveDbg(value.data[i], aStream, indentp, path+"."+key+"[%i]" % i, nbp)
283         continue
284       if "Expression" in strType:
285         try:
286           evaluate = value.evaluate(config)
287           aStream.write("%s%s.%s : %s --> '%s'\n" % (indstr, path, key, str(value), evaluate))
288         except Exception as e:      
289           aStream.write("%s%s.%s : !!! ERROR: %s !!!\n" % (indstr, path, key, e.message))     
290         continue
291       if "Reference" in strType:
292         try:
293           evaluate = value.resolve(config)
294           aStream.write("%s%s.%s : %s --> '%s'\n" % (indstr, path, key, str(value), evaluate))
295         except Exception as e:  
296           aStream.write("%s%s.%s : !!! ERROR: %s !!!\n" % (indstr, path, key, e.message))     
297         continue
298       if type(value) in [str, bool, int, type(None), unicode]:
299         aStream.write("%s%s.%s : '%s'\n" % (indstr, path, key, str(value)))
300         continue
301       try:
302         aStream.write("!!! TODO fix that %s %s%s.%s : %s\n" % (type(value), indstr, path, key, str(value)))
303       except Exception as e:      
304         aStream.write("%s%s.%s : !!! %s\n" % (indstr, path, key, e.message))