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