]> SALOME platform Git repositories - tools/eficas.git/blob - Noyau/N_info.py
Salome HOME
premiere version
[tools/eficas.git] / Noyau / N_info.py
1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2013   EDF R&D
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 """Module to manage information printing : debug, info, error.
22 Should replace 'print' and 'UTMESS' calls at least in the supervisor
23 modules.
24 Only used for debug right now.
25 """
26
27 import os
28 import os.path as osp
29 import re
30 import traceback
31 from functools import partial
32 from subprocess import Popen, PIPE
33
34 from N_utils import Enum, Singleton
35 from strfunc import convert
36
37 def default_print(text):
38     """Basic print function."""
39     print convert(text)
40
41 LEVEL = Enum(
42     'DEBUG',
43     'INFO',
44     'WARN',
45     'ERROR'
46 )
47
48 class Category(object):
49     """Define a category of message for different parts of the code.
50     This allows to store different parameters for each category of message."""
51     def __init__(self):
52         self._level = LEVEL.INFO
53         self._fmt = "%-8s"
54         self._header = {
55             LEVEL.DEBUG : "DEBUG",
56             LEVEL.INFO : None,
57             LEVEL.WARN : "WARNING",
58             LEVEL.ERROR : "ERROR",
59         }
60
61     def set_level(self, level):
62         """Set the current level."""
63         self._level = level
64
65     def get_level(self):
66         """Return the current level."""
67         return self._level
68
69     def set_header(self, level, header):
70         """Set the header of ``level`` messages."""
71         self._header[level] = header
72
73     def get_header(self, level):
74         """Return the header at this ``level``."""
75         header = self._header.get(level, "")
76         if header:
77             header = self._fmt % header
78         return header
79
80     def active(self, level):
81         """Tell if a message should be print at this ``level``."""
82         return self._level <= level
83
84
85 ALL = Category()
86 SUPERV = Category()
87 SUPERV.set_header(LEVEL.ERROR, None)
88 MISS = Category()
89
90 REGEXP_ORIG = re.compile('File [\'\"]*(.*?)[\'\"]*, *line ([0-9]+), *in (.*)')
91
92 # slighty different and very simplier than logger objects
93 # from the logging module.
94 class InfoLevel(Singleton):
95     """Store informations level."""
96     _singleton_id = 'N_info.InfoLevel'
97     
98     def __init__(self, level):
99         """Initialization"""
100         self._parts = []
101         for part in self._parts:
102             part.level = level
103         self.reset_print_function()
104         self._msg_callback = []
105         #self.extend_message(ALL, stack_header_callback)
106         self.extend_message(ALL, insert_header)
107
108     def add(self, category):
109         """Add a category of message."""
110         self._parts.append(category)
111
112     def set_level(self, category, level):
113         """Set the current level for ``category``."""
114         assert category in self._parts, "unknown category : %s" % category
115         assert LEVEL.exists(level), "unknown level : %s" % level
116         category.set_level(level)
117         if category == ALL:
118             for part in self._parts:
119                 part.set_level(level)
120
121     def set_debug(self):
122         """Set debug level for all categories."""
123         self.set_level(ALL, LEVEL.DEBUG)
124
125     def set_header(self, category, level, header):
126         """Set the header of ``level`` messages."""
127         category.set_header(level, header)
128
129     def register_print_function(self, print_function):
130         """Define the `print_function` to use."""
131         self._print = print_function
132
133     def reset_print_function(self):
134         """Register the default 'print function'."""
135         self._print = default_print
136
137     def extend_message(self, category, callback):
138         """Allow to extend the message calling an external function."""
139         self._msg_callback.append((category, callback))
140
141     def _message(self, category, level, msg, args, kwargs):
142         """Print the message if the level is reached."""
143         if category.active(level):
144             if kwargs.get('utmess'):
145                 func = self._message_utmess
146             else:
147                 func = self._message_print
148             func = self._message_print
149             apply(func, (category, level, msg, args, kwargs))
150
151     def _message_print(self, category, level, msg, args, kwargs):
152         """Print the message if the level is reached."""
153         for cat, cbk in self._msg_callback:
154             if cat in (ALL, category):
155                 msg, args = cbk(category, level, msg, args, kwargs)
156         if len(args) > 0:
157             try:
158                 msg = msg % args
159             except Exception, err:
160                 msg = repr((msg, args, err))
161         self._print(msg)
162
163     def _message_utmess(self, category, level, msg, args, kwargs):
164         """Print the message if the level is reached."""
165         # how to use callbacks ? valk ?
166         from Utilitai.Utmess import MessageLog
167         code = {
168             LEVEL.DEBUG : 'I',
169             LEVEL.INFO : 'I',
170             LEVEL.WARN : 'A',
171             LEVEL.ERROR : 'F',
172         }
173         valk = kwargs.get('valk', ())
174         vali = kwargs.get('vali', ())
175         valr = kwargs.get('valr', ())
176         msg = MessageLog.GetText(code[level], msg, valk, vali, valr)
177         for cat, cbk in self._msg_callback:
178             if cat in (ALL, category):
179                 msg, args = cbk(category, level, msg, args, kwargs)
180         self._print(msg)
181
182     def debug(self, category, msg, *args, **kwargs):
183         """Print a debug message."""
184         self._message(category or ALL, LEVEL.DEBUG, msg, args, kwargs)
185
186     def info(self, category, msg, *args, **kwargs):
187         """Print an information message."""
188         self._message(category or ALL, LEVEL.INFO, msg, args, kwargs)
189
190     def warn(self, category, msg, *args, **kwargs):
191         """Print a warning message."""
192         self._message(category or ALL, LEVEL.WARN, msg, args, kwargs)
193
194     def error(self, category, msg, *args, **kwargs):
195         """Print an error message."""
196         self._message(category or ALL, LEVEL.ERROR, msg, args, kwargs)
197
198     critical = error
199
200     def add_memory_info(self, category):
201         """Shortcut to add memory informations."""
202         self.extend_message(category, mem_msg_callback)
203
204     def use_aster_print(self):
205         """Shortcut to use aster.affiche function to print the messages."""
206         import aster
207         self.register_print_function(partial(aster.affiche, 'MESSAGE'))
208
209
210 # defined extensions
211 def insert_header(category, level, msg, args, kwargs):
212     """Insert the header."""
213     header = category.get_header(level)
214     if header:
215         msg = header + msg
216     return msg, args
217
218 def stack_header_callback(category, level, msg, args, kwargs):
219     """To insert the origin."""
220     if level <= LEVEL.DEBUG:
221         stack_id = -5 + kwargs.get('stack_id', 0)
222         stack = traceback.format_stack(limit=10)[stack_id]
223         mat = REGEXP_ORIG.search(stack)
224         origin = '[%s:%s in %s] ' % (osp.basename(mat.group(1)), mat.group(2), mat.group(3))
225         msg = origin + msg
226     return msg, args
227
228
229 # objet singleton
230 message = InfoLevel(LEVEL.INFO)
231 message.add(ALL)
232 message.add(SUPERV)
233 message.add(MISS)
234
235 # callback to add memory information
236 _pid = os.getpid()
237
238 RE_VMPEAK = re.compile('VmPeak:\s*([0-9]+)\s*([kMGBo]+)', re.M | re.I)
239
240 def memory_used(pid):
241     """Return the current VmPeak value."""
242     p = Popen(['cat', '/proc/%s/status' % pid], stdout=PIPE)
243     output = p.communicate()[0]
244     mat = RE_VMPEAK.search(output)
245     mem = mat and int(mat.group(1)) or 0.
246     return mem / 1024.
247
248 current_memory_used = partial(memory_used, _pid)
249
250 def mem_msg_callback(category, level, msg, args, kwargs):
251     """Callback to add memory infos to message."""
252     if level <= LEVEL.DEBUG:
253         msg = msg + " - VmPeak : %.2f Mo"
254         args = tuple(list(args) + [current_memory_used(), ])
255     return msg, args
256
257
258 if __name__ == "__main__":
259     message.set_level(SUPERV, LEVEL.WARN)
260     message.set_level(MISS, LEVEL.DEBUG)
261     #message.debug(None, "debug message")
262     message.info(ALL, "information message")
263     message.warn(None, "warning message")
264     message.error(ALL, "error message")
265     message.add_memory_info()
266     #message.debug(MISS, "debug supervisor message")
267     message.info(SUPERV, "information supervisor message")
268     message.warn(SUPERV, "warning supervisor message")
269     message.error(SUPERV, "error supervisor message")
270     message.critical(MISS, "test the critical alias")
271
272