Salome HOME
gestion des listes et label sur 2
[tools/eficas.git] / Noyau / N_info.py
1 # coding=utf-8
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
38 def default_print(text):
39     """Basic print function."""
40     print convert(text)
41
42 LEVEL = Enum(
43     'DEBUG',
44     'INFO',
45     'WARN',
46     'ERROR'
47 )
48
49
50 class Category(object):
51
52     """Define a category of message for different parts of the code.
53     This allows to store different parameters for each category of message."""
54
55     def __init__(self):
56         self._level = LEVEL.INFO
57         self._fmt = "%-8s"
58         self._header = {
59             LEVEL.DEBUG: "DEBUG",
60             LEVEL.INFO: None,
61             LEVEL.WARN: "WARNING",
62             LEVEL.ERROR: "ERROR",
63         }
64
65     def set_level(self, level):
66         """Set the current level."""
67         self._level = level
68
69     def get_level(self):
70         """Return the current level."""
71         return self._level
72
73     def set_header(self, level, header):
74         """Set the header of ``level`` messages."""
75         self._header[level] = header
76
77     def get_header(self, level):
78         """Return the header at this ``level``."""
79         header = self._header.get(level, "")
80         if header:
81             header = self._fmt % header
82         return header
83
84     def active(self, level):
85         """Tell if a message should be print at this ``level``."""
86         return self._level <= level
87
88
89 ALL = Category()
90 SUPERV = Category()
91 SUPERV.set_header(LEVEL.ERROR, None)
92 MISS = Category()
93
94 REGEXP_ORIG = re.compile('File [\'\"]*(.*?)[\'\"]*, *line ([0-9]+), *in (.*)')
95
96 # slighty different and very simplier than logger objects
97 # from the logging module.
98
99
100 class InfoLevel(Singleton):
101
102     """Store informations level."""
103     _singleton_id = 'N_info.InfoLevel'
104
105     def __init__(self, level):
106         """Initialization"""
107         self._parts = []
108         for part in self._parts:
109             part.level = level
110         self.reset_print_function()
111         self._msg_callback = []
112         # self.extend_message(ALL, stack_header_callback)
113         self.extend_message(ALL, insert_header)
114
115     def add(self, category):
116         """Add a category of message."""
117         self._parts.append(category)
118
119     def set_level(self, category, level):
120         """Set the current level for ``category``."""
121         assert category in self._parts, "unknown category : %s" % category
122         assert LEVEL.exists(level), "unknown level : %s" % level
123         category.set_level(level)
124         if category == ALL:
125             for part in self._parts:
126                 part.set_level(level)
127
128     def set_debug(self):
129         """Set debug level for all categories."""
130         self.set_level(ALL, LEVEL.DEBUG)
131
132     def set_header(self, category, level, header):
133         """Set the header of ``level`` messages."""
134         category.set_header(level, header)
135
136     def register_print_function(self, print_function):
137         """Define the `print_function` to use."""
138         self._print = print_function
139
140     def reset_print_function(self):
141         """Register the default 'print function'."""
142         self._print = default_print
143
144     def extend_message(self, category, callback):
145         """Allow to extend the message calling an external function."""
146         self._msg_callback.append((category, callback))
147
148     def _message(self, category, level, msg, args, kwargs):
149         """Print the message if the level is reached."""
150         if category.active(level):
151             if kwargs.get('utmess'):
152                 func = self._message_utmess
153             else:
154                 func = self._message_print
155             func = self._message_print
156             apply(func, (category, level, msg, args, kwargs))
157
158     def _message_print(self, category, level, msg, args, kwargs):
159         """Print the message if the level is reached."""
160         for cat, cbk in self._msg_callback:
161             if cat in (ALL, category):
162                 msg, args = cbk(category, level, msg, args, kwargs)
163         if len(args) > 0:
164             try:
165                 msg = msg % args
166             except Exception, err:
167                 msg = repr((msg, args, err))
168         self._print(msg)
169
170     def _message_utmess(self, category, level, msg, args, kwargs):
171         """Print the message if the level is reached."""
172         # how to use callbacks ? valk ?
173         from Utilitai.Utmess import MessageLog
174         code = {
175             LEVEL.DEBUG: 'I',
176             LEVEL.INFO: 'I',
177             LEVEL.WARN: 'A',
178             LEVEL.ERROR: 'F',
179         }
180         valk = kwargs.get('valk', ())
181         vali = kwargs.get('vali', ())
182         valr = kwargs.get('valr', ())
183         msg = MessageLog.GetText(code[level], msg, valk, vali, valr)
184         for cat, cbk in self._msg_callback:
185             if cat in (ALL, category):
186                 msg, args = cbk(category, level, msg, args, kwargs)
187         self._print(msg)
188
189     def debug(self, category, msg, *args, **kwargs):
190         """Print a debug message."""
191         self._message(category or ALL, LEVEL.DEBUG, msg, args, kwargs)
192
193     def info(self, category, msg, *args, **kwargs):
194         """Print an information message."""
195         self._message(category or ALL, LEVEL.INFO, msg, args, kwargs)
196
197     def warn(self, category, msg, *args, **kwargs):
198         """Print a warning message."""
199         self._message(category or ALL, LEVEL.WARN, msg, args, kwargs)
200
201     def error(self, category, msg, *args, **kwargs):
202         """Print an error message."""
203         self._message(category or ALL, LEVEL.ERROR, msg, args, kwargs)
204
205     critical = error
206
207     def add_memory_info(self, category):
208         """Shortcut to add memory informations."""
209         self.extend_message(category, mem_msg_callback)
210
211     def use_aster_print(self):
212         """Shortcut to use aster.affiche function to print the messages."""
213         import aster
214         self.register_print_function(partial(aster.affiche, 'MESSAGE'))
215
216
217 # defined extensions
218 def insert_header(category, level, msg, args, kwargs):
219     """Insert the header."""
220     header = category.get_header(level)
221     if header:
222         msg = header + msg
223     return msg, args
224
225
226 def stack_header_callback(category, level, msg, args, kwargs):
227     """To insert the origin."""
228     if level <= LEVEL.DEBUG:
229         stack_id = -5 + kwargs.get('stack_id', 0)
230         stack = traceback.format_stack(limit=10)[stack_id]
231         mat = REGEXP_ORIG.search(stack)
232         origin = '[%s:%s in %s] ' % (
233             osp.basename(mat.group(1)), mat.group(2), mat.group(3))
234         msg = origin + msg
235     return msg, args
236
237
238 # objet singleton
239 message = InfoLevel(LEVEL.INFO)
240 message.add(ALL)
241 message.add(SUPERV)
242 message.add(MISS)
243
244 # callback to add memory information
245 _pid = os.getpid()
246
247 RE_VMPEAK = re.compile('VmPeak:\s*([0-9]+)\s*([kMGBo]+)', re.M | re.I)
248
249
250 def memory_used(pid):
251     """Return the current VmPeak value."""
252     p = Popen(['cat', '/proc/%s/status' % pid], stdout=PIPE)
253     output = p.communicate()[0]
254     mat = RE_VMPEAK.search(output)
255     mem = mat and int(mat.group(1)) or 0.
256     return mem / 1024.
257
258 current_memory_used = partial(memory_used, _pid)
259
260
261 def mem_msg_callback(category, level, msg, args, kwargs):
262     """Callback to add memory infos to message."""
263     if level <= LEVEL.DEBUG:
264         msg = msg + " - VmPeak : %.2f Mo"
265         args = tuple(list(args) + [current_memory_used(), ])
266     return msg, args
267
268
269 if __name__ == "__main__":
270     message.set_level(SUPERV, LEVEL.WARN)
271     message.set_level(MISS, LEVEL.DEBUG)
272     # message.debug(None, "debug message")
273     message.info(ALL, "information message")
274     message.warn(None, "warning message")
275     message.error(ALL, "error message")
276     message.add_memory_info()
277     # message.debug(MISS, "debug supervisor message")
278     message.info(SUPERV, "information supervisor message")
279     message.warn(SUPERV, "warning supervisor message")
280     message.error(SUPERV, "error supervisor message")
281     message.critical(MISS, "test the critical alias")