msger.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #!/usr/bin/env 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. import os,sys
  19. import re
  20. import time
  21. __ALL__ = ['set_mode',
  22. 'get_loglevel',
  23. 'set_loglevel',
  24. 'set_logfile',
  25. 'raw',
  26. 'debug',
  27. 'verbose',
  28. 'info',
  29. 'warning',
  30. 'error',
  31. 'ask',
  32. 'pause',
  33. ]
  34. # COLORs in ANSI
  35. INFO_COLOR = 32 # green
  36. WARN_COLOR = 33 # yellow
  37. ERR_COLOR = 31 # red
  38. ASK_COLOR = 34 # blue
  39. NO_COLOR = 0
  40. PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S)
  41. INTERACTIVE = True
  42. LOG_LEVEL = 1
  43. LOG_LEVELS = {
  44. 'quiet': 0,
  45. 'normal': 1,
  46. 'verbose': 2,
  47. 'debug': 3,
  48. 'never': 4,
  49. }
  50. LOG_FILE_FP = None
  51. LOG_CONTENT = ''
  52. CATCHERR_BUFFILE_FD = -1
  53. CATCHERR_BUFFILE_PATH = None
  54. CATCHERR_SAVED_2 = -1
  55. def _general_print(head, color, msg = None, stream = None, level = 'normal'):
  56. global LOG_CONTENT
  57. if not stream:
  58. stream = sys.stdout
  59. if LOG_LEVELS[level] > LOG_LEVEL:
  60. # skip
  61. return
  62. # encode raw 'unicode' str to utf8 encoded str
  63. if msg and isinstance(msg, unicode):
  64. msg = msg.encode('utf-8', 'ignore')
  65. errormsg = ''
  66. if CATCHERR_BUFFILE_FD > 0:
  67. size = os.lseek(CATCHERR_BUFFILE_FD , 0, os.SEEK_END)
  68. os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET)
  69. errormsg = os.read(CATCHERR_BUFFILE_FD, size)
  70. os.ftruncate(CATCHERR_BUFFILE_FD, 0)
  71. # append error msg to LOG
  72. if errormsg:
  73. LOG_CONTENT += errormsg
  74. # append normal msg to LOG
  75. save_msg = msg.strip() if msg else None
  76. if save_msg:
  77. timestr = time.strftime("[%m/%d %H:%M:%S %Z] ", time.localtime())
  78. LOG_CONTENT += timestr + save_msg + '\n'
  79. if errormsg:
  80. _color_print('', NO_COLOR, errormsg, stream, level)
  81. _color_print(head, color, msg, stream, level)
  82. def _color_print(head, color, msg, stream, level):
  83. colored = True
  84. if color == NO_COLOR or \
  85. not stream.isatty() or \
  86. os.getenv('ANSI_COLORS_DISABLED') is not None:
  87. colored = False
  88. if head.startswith('\r'):
  89. # need not \n at last
  90. newline = False
  91. else:
  92. newline = True
  93. if colored:
  94. head = '\033[%dm%s:\033[0m ' %(color, head)
  95. if not newline:
  96. # ESC cmd to clear line
  97. head = '\033[2K' + head
  98. else:
  99. if head:
  100. head += ': '
  101. if head.startswith('\r'):
  102. head = head.lstrip()
  103. newline = True
  104. if msg is not None:
  105. if isinstance(msg, unicode):
  106. msg = msg.encode('utf8', 'ignore')
  107. stream.write('%s%s' % (head, msg))
  108. if newline:
  109. stream.write('\n')
  110. stream.flush()
  111. def _color_perror(head, color, msg, level = 'normal'):
  112. if CATCHERR_BUFFILE_FD > 0:
  113. _general_print(head, color, msg, sys.stdout, level)
  114. else:
  115. _general_print(head, color, msg, sys.stderr, level)
  116. def _split_msg(head, msg):
  117. if isinstance(msg, list):
  118. msg = '\n'.join(map(str, msg))
  119. if msg.startswith('\n'):
  120. # means print \n at first
  121. msg = msg.lstrip()
  122. head = '\n' + head
  123. elif msg.startswith('\r'):
  124. # means print \r at first
  125. msg = msg.lstrip()
  126. head = '\r' + head
  127. m = PREFIX_RE.match(msg)
  128. if m:
  129. head += ' <%s>' % m.group(1)
  130. msg = m.group(2)
  131. return head, msg
  132. def get_loglevel():
  133. return (k for k,v in LOG_LEVELS.items() if v==LOG_LEVEL).next()
  134. def set_loglevel(level):
  135. global LOG_LEVEL
  136. if level not in LOG_LEVELS:
  137. # no effect
  138. return
  139. LOG_LEVEL = LOG_LEVELS[level]
  140. def set_interactive(mode=True):
  141. global INTERACTIVE
  142. if mode:
  143. INTERACTIVE = True
  144. else:
  145. INTERACTIVE = False
  146. def log(msg=''):
  147. # log msg to LOG_CONTENT then save to logfile
  148. global LOG_CONTENT
  149. if msg:
  150. LOG_CONTENT += msg
  151. def raw(msg=''):
  152. _general_print('', NO_COLOR, msg)
  153. def info(msg):
  154. head, msg = _split_msg('Info', msg)
  155. _general_print(head, INFO_COLOR, msg)
  156. def verbose(msg):
  157. head, msg = _split_msg('Verbose', msg)
  158. _general_print(head, INFO_COLOR, msg, level = 'verbose')
  159. def warning(msg):
  160. head, msg = _split_msg('Warning', msg)
  161. _color_perror(head, WARN_COLOR, msg)
  162. def debug(msg):
  163. head, msg = _split_msg('Debug', msg)
  164. _color_perror(head, ERR_COLOR, msg, level = 'debug')
  165. def error(msg):
  166. head, msg = _split_msg('Error', msg)
  167. _color_perror(head, ERR_COLOR, msg)
  168. sys.exit(1)
  169. def ask(msg, default=True):
  170. _general_print('\rQ', ASK_COLOR, '')
  171. try:
  172. if default:
  173. msg += '(Y/n) '
  174. else:
  175. msg += '(y/N) '
  176. if INTERACTIVE:
  177. while True:
  178. repl = raw_input(msg)
  179. if repl.lower() == 'y':
  180. return True
  181. elif repl.lower() == 'n':
  182. return False
  183. elif not repl.strip():
  184. # <Enter>
  185. return default
  186. # else loop
  187. else:
  188. if default:
  189. msg += ' Y'
  190. else:
  191. msg += ' N'
  192. _general_print('', NO_COLOR, msg)
  193. return default
  194. except KeyboardInterrupt:
  195. sys.stdout.write('\n')
  196. sys.exit(2)
  197. def choice(msg, choices, default=0):
  198. if default >= len(choices):
  199. return None
  200. _general_print('\rQ', ASK_COLOR, '')
  201. try:
  202. msg += " [%s] " % '/'.join(choices)
  203. if INTERACTIVE:
  204. while True:
  205. repl = raw_input(msg)
  206. if repl in choices:
  207. return repl
  208. elif not repl.strip():
  209. return choices[default]
  210. else:
  211. msg += choices[default]
  212. _general_print('', NO_COLOR, msg)
  213. return choices[default]
  214. except KeyboardInterrupt:
  215. sys.stdout.write('\n')
  216. sys.exit(2)
  217. def pause(msg=None):
  218. if INTERACTIVE:
  219. _general_print('\rQ', ASK_COLOR, '')
  220. if msg is None:
  221. msg = 'press <ENTER> to continue ...'
  222. raw_input(msg)
  223. def set_logfile(fpath):
  224. global LOG_FILE_FP
  225. def _savelogf():
  226. if LOG_FILE_FP:
  227. fp = open(LOG_FILE_FP, 'w')
  228. fp.write(LOG_CONTENT)
  229. fp.close()
  230. if LOG_FILE_FP is not None:
  231. warning('duplicate log file configuration')
  232. LOG_FILE_FP = fpath
  233. import atexit
  234. atexit.register(_savelogf)
  235. def enable_logstderr(fpath):
  236. global CATCHERR_BUFFILE_FD
  237. global CATCHERR_BUFFILE_PATH
  238. global CATCHERR_SAVED_2
  239. if os.path.exists(fpath):
  240. os.remove(fpath)
  241. CATCHERR_BUFFILE_PATH = fpath
  242. CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT)
  243. CATCHERR_SAVED_2 = os.dup(2)
  244. os.dup2(CATCHERR_BUFFILE_FD, 2)
  245. def disable_logstderr():
  246. global CATCHERR_BUFFILE_FD
  247. global CATCHERR_BUFFILE_PATH
  248. global CATCHERR_SAVED_2
  249. raw(msg = None) # flush message buffer and print it.
  250. os.dup2(CATCHERR_SAVED_2, 2)
  251. os.close(CATCHERR_SAVED_2)
  252. os.close(CATCHERR_BUFFILE_FD)
  253. os.unlink(CATCHERR_BUFFILE_PATH)
  254. CATCHERR_BUFFILE_FD = -1
  255. CATCHERR_BUFFILE_PATH = None
  256. CATCHERR_SAVED_2 = -1