Merge remote branch 'origin/master'
[tools/mic.git] / mic / msger.py
1 #!/usr/bin/python -tt
2 # vim: ai ts=4 sts=4 et sw=4
3 #
4 # Copyright (c) 2009, 2010, 2011 Intel, Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the Free
8 # Software Foundation; version 2 of the License
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 # for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc., 59
17 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 import os,sys
20 import re
21 from datetime import datetime
22
23 __ALL__ = ['set_mode',
24            'get_loglevel',
25            'set_loglevel',
26            'set_logfile',
27            'raw',
28            'debug',
29            'verbose',
30            'info',
31            'warning',
32            'error',
33            'ask',
34            'pause',
35           ]
36
37 # COLORs in ANSI
38 INFO_COLOR = 32 # green
39 WARN_COLOR = 33 # yellow
40 ERR_COLOR  = 31 # red
41 ASK_COLOR  = 34 # blue
42 NO_COLOR = 0
43
44 PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S)
45
46 INTERACTIVE = True
47
48 LOG_LEVEL = 1
49 LOG_LEVELS = {
50                 'quiet': 0,
51                 'normal': 1,
52                 'verbose': 2,
53                 'debug': 3,
54                 'never': 4,
55              }
56
57 LOG_FILE_FP = None
58 LOG_CONTENT = ''
59 CATCHERR_BUFFILE_FD = -1
60 CATCHERR_BUFFILE_PATH = None
61 CATCHERR_SAVED_2 = -1
62
63 def _general_print(head, color, msg = None, stream = None, level = 'normal'):
64     global LOG_CONTENT
65     if not stream:
66         stream = sys.stdout
67
68     if LOG_LEVELS[level] > LOG_LEVEL:
69         # skip
70         return
71
72     errormsg = ''
73     if CATCHERR_BUFFILE_FD > 0:
74         size = os.lseek(CATCHERR_BUFFILE_FD , 0, os.SEEK_END)
75         os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET)
76         errormsg = os.read(CATCHERR_BUFFILE_FD, size)
77         os.ftruncate(CATCHERR_BUFFILE_FD, 0)
78
79     if LOG_FILE_FP:
80         if errormsg:
81             LOG_CONTENT += errormsg
82
83         save_msg = msg.strip()
84         if save_msg:
85             timestr = datetime.now().strftime('[%m/%d %H:%M:%S] ')
86             LOG_CONTENT += timestr + save_msg + '\n'
87
88     if errormsg:
89         _color_print('', NO_COLOR, errormsg, stream, level)
90
91     _color_print(head, color, msg, stream, level)
92
93 def _color_print(head, color, msg, stream, level):
94     colored = True
95     if color == NO_COLOR or \
96        not stream.isatty() or \
97        os.getenv('ANSI_COLORS_DISABLED') is not None:
98         colored = False
99
100     if head.startswith('\r'):
101         # need not \n at last
102         newline = False
103     else:
104         newline = True
105
106     if colored:
107         head = '\033[%dm%s:\033[0m ' %(color, head)
108         if not newline:
109             # ESC cmd to clear line
110             head = '\033[2K' + head
111     else:
112         if head:
113             head += ': '
114             if head.startswith('\r'):
115                 head = head.lstrip()
116                 newline = True
117
118     if msg is not None:
119         stream.write('%s%s' % (head, msg))
120         if newline:
121             stream.write('\n')
122
123     stream.flush()
124
125 def _color_perror(head, color, msg, level = 'normal'):
126     if CATCHERR_BUFFILE_FD > 0:
127         _general_print(head, color, msg, sys.stdout, level)
128     else:
129         _general_print(head, color, msg, sys.stderr, level)
130
131 def _split_msg(head, msg):
132     if isinstance(msg, list):
133         msg = '\n'.join(map(str, msg))
134
135     if msg.startswith('\n'):
136         # means print \n at first
137         msg = msg.lstrip()
138         head = '\n' + head
139
140     elif msg.startswith('\r'):
141         # means print \r at first
142         msg = msg.lstrip()
143         head = '\r' + head
144
145     m = PREFIX_RE.match(msg)
146     if m:
147         head += ' <%s>' % m.group(1)
148         msg = m.group(2)
149
150     return head, msg
151
152 def get_loglevel():
153     return (k for k,v in LOG_LEVELS.items() if v==LOG_LEVEL).next()
154
155 def set_loglevel(level):
156     global LOG_LEVEL
157     if level not in LOG_LEVELS:
158         # no effect
159         return
160
161     LOG_LEVEL = LOG_LEVELS[level]
162
163 def set_interactive(mode=True):
164     global INTERACTIVE
165     if mode:
166         INTERACTIVE = True
167     else:
168         INTERACTIVE = False
169
170 def raw(msg=''):
171     _general_print('', NO_COLOR, msg)
172
173 def info(msg):
174     head, msg = _split_msg('Info', msg)
175     _general_print(head, INFO_COLOR, msg)
176
177 def verbose(msg):
178     head, msg = _split_msg('Verbose', msg)
179     _general_print(head, INFO_COLOR, msg, level = 'verbose')
180
181 def warning(msg):
182     head, msg = _split_msg('Warning', msg)
183     _color_perror(head, WARN_COLOR, msg)
184
185 def debug(msg):
186     head, msg = _split_msg('Debug', msg)
187     _color_perror(head, ERR_COLOR, msg, level = 'debug')
188
189 def error(msg):
190     head, msg = _split_msg('Error', msg)
191     _color_perror(head, ERR_COLOR, msg)
192     sys.exit(1)
193
194 def ask(msg, default=True):
195     _general_print('\rQ', ASK_COLOR, '')
196     try:
197         if default:
198             msg += '(Y/n) '
199         else:
200             msg += '(y/N) '
201         if INTERACTIVE:
202             while True:
203                 repl = raw_input(msg)
204                 if repl.lower() == 'y':
205                     return True
206                 elif repl.lower() == 'n':
207                     return False
208                 elif not repl.strip():
209                     # <Enter>
210                     return default
211
212                 # else loop
213         else:
214             if default:
215                 msg += ' Y'
216             else:
217                 msg += ' N'
218             _general_print('', NO_COLOR, msg)
219
220             return default
221     except KeyboardInterrupt:
222         sys.stdout.write('\n')
223         sys.exit(2)
224
225 def pause(msg=None):
226     if INTERACTIVE:
227         _general_print('\rQ', ASK_COLOR, '')
228         if msg is None:
229             msg = 'press <ENTER> to continue ...'
230         raw_input(msg)
231
232 def set_logfile(fpath):
233     global LOG_FILE_FP
234
235     def _savelogf():
236         if LOG_FILE_FP:
237             if not os.path.exists(os.path.dirname(LOG_FILE_FP)):
238                 os.makedirs(os.path.dirname(LOG_FILE_FP))
239             fp = open(LOG_FILE_FP, 'w')
240             fp.write(LOG_CONTENT)
241             fp.close()
242
243     if LOG_FILE_FP is not None:
244         warning('duplicate log file configuration')
245
246     LOG_FILE_FP = os.path.abspath(os.path.expanduser(fpath))
247
248     import atexit
249     atexit.register(_savelogf)
250
251 def enable_logstderr(fpath):
252     global CATCHERR_BUFFILE_FD
253     global CATCHERR_BUFFILE_PATH
254     global CATCHERR_SAVED_2
255
256     if os.path.exists(fpath):
257         os.remove(fpath)
258     CATCHERR_BUFFILE_PATH = fpath
259     CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT)
260     CATCHERR_SAVED_2 = os.dup(2)
261     os.dup2(CATCHERR_BUFFILE_FD, 2)
262
263 def disable_logstderr():
264     global CATCHERR_BUFFILE_FD
265     global CATCHERR_BUFFILE_PATH
266     global CATCHERR_SAVED_2
267
268     os.dup2(CATCHERR_SAVED_2, 2)
269     os.close(CATCHERR_SAVED_2)
270     os.close(CATCHERR_BUFFILE_FD)
271     os.unlink(CATCHERR_BUFFILE_PATH)
272     CATCHERR_BUFFILE_FD = -1
273     CATCHERR_BUFFILE_PATH = None
274     CATCHERR_SAVED_2 = -1