PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/LabController/src/bkr/labcontroller/tback.py

https://github.com/beaker-project/beaker
Python | 268 lines | 233 code | 3 blank | 32 comment | 1 complexity | 84450db2c49bc54206a31541c1ca1648 MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2009 Red Hat, Inc.
  3. #
  4. # Copyright (c) Django Software Foundation and individual contributors.
  5. # All rights reserved.
  6. #
  7. # Redistribution and use in source and binary forms, with or without modification,
  8. # are permitted provided that the following conditions are met:
  9. #
  10. # 1. Redistributions of source code must retain the above copyright notice,
  11. # this list of conditions and the following disclaimer.
  12. #
  13. # 2. Redistributions in binary form must reproduce the above copyright
  14. # notice, this list of conditions and the following disclaimer in the
  15. # documentation and/or other materials provided with the distribution.
  16. #
  17. # 3. Neither the name of Django nor the names of its contributors may be used
  18. # to endorse or promote products derived from this software without
  19. # specific prior written permission.
  20. #
  21. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  22. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  23. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  24. # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  25. # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  26. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  27. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  28. # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. # This is a copy and paste of kobo.tback version 0.4.2-1
  32. import os
  33. import sys
  34. import traceback
  35. import re
  36. def get_traceback():
  37. """Return a traceback string."""
  38. exc_info = sys.exc_info()
  39. if exc_info[0] is None:
  40. return ""
  41. return "".join(traceback.format_exception(*exc_info)).replace(r"\n", "\n")
  42. def get_exception():
  43. """Return an exception string."""
  44. result = sys.exc_info()[1]
  45. if result is None:
  46. return ""
  47. return str(result)
  48. class Traceback(object):
  49. """Enhanced traceback with detailed output."""
  50. def __init__(self, exc_info=None, show_traceback=True, show_code=True, show_locals=True, show_environ=False, show_modules=False):
  51. self.exc_info = exc_info or sys.exc_info()
  52. self.show_traceback = show_traceback
  53. self.show_code = show_code
  54. self.show_locals = show_locals
  55. self.show_environ = show_environ
  56. self.show_modules = show_modules
  57. def _to_str(self, value, format=None):
  58. """Convert value to string.
  59. We must absolutely avoid propagating exceptions, and str(value)
  60. COULD cause any exception, so we MUST catch any...
  61. """
  62. format = format or "%s"
  63. try:
  64. result = format % value
  65. except:
  66. result = "<ERROR WHILE CONVERTING VALUE TO STRING>"
  67. return result
  68. def get_traceback(self):
  69. """Return a traceback string."""
  70. if self.exc_info[0] is None:
  71. return ""
  72. result = []
  73. if self.show_traceback:
  74. c_pattern = re.compile('\n')
  75. for i in traceback.format_exception(*self.exc_info):
  76. for line in c_pattern.split(i):
  77. line and result.append(line)
  78. if self.show_environ:
  79. result.append("<ENVIRON>")
  80. for key, value in sorted(os.environ.iteritems()):
  81. result.append("%s = %s" % (self._to_str(key, "%20s"), self._to_str(value)))
  82. result.append("</ENVIRON>")
  83. if self.show_environ:
  84. result.append("<GLOBALS>")
  85. for key, value in sorted(os.environ.iteritems()):
  86. result.append("%s = %s" % (self._to_str(key, "%20s"), self._to_str(value)))
  87. result.append("</GLOBALS>")
  88. if self.show_modules:
  89. result.append("<MODULES>")
  90. for key, value in sorted(sys.modules.iteritems()):
  91. result.append("%s = %s" % (self._to_str(key, "%20"), self._to_str(value)))
  92. result.append("</MODULES>")
  93. if self.show_code or self.show_locals:
  94. for frame in reversed(self.get_frames()):
  95. if frame["filename"] == "?" or frame["lineno"] == "?":
  96. continue
  97. result.append("Frame %s in %s at line %s" % (
  98. self._to_str(frame["function"]),
  99. self._to_str(frame["filename"]),
  100. self._to_str(frame["lineno"]))
  101. )
  102. if self.show_code:
  103. result.append("<CODE>")
  104. lineno = frame["pre_context_lineno"]
  105. for line in frame["pre_context"]:
  106. result.append(" %s %s" % (self._to_str(lineno, "%4d"), self._to_str(line)))
  107. lineno += 1
  108. result.append("--> %s %s" % (self._to_str(lineno, "%4d"), self._to_str(frame["context_line"])))
  109. lineno += 1
  110. for line in frame["post_context"]:
  111. result.append(" %s %s" % (self._to_str(lineno, "%4d"), self._to_str(line)))
  112. lineno += 1
  113. result.append("</CODE>")
  114. if self.show_locals:
  115. result.append("<LOCALS>")
  116. for key, value in sorted(frame["vars"]):
  117. result.append("%20s = %s" % (self._to_str(key), self._to_str(value, "%.200r")))
  118. if key == "self":
  119. try:
  120. variables = sorted(dir(value))
  121. except Exception, ex:
  122. # this exception may be thrown on xmlrpc proxy object
  123. # e.g. <ServerProxy>.__dir__ will be handled as xmlrpc call,
  124. # expected behaviour is that the call returns list of strings for the scope
  125. variables = []
  126. for obj_key in variables:
  127. try:
  128. obj_value = getattr(value, obj_key)
  129. except:
  130. result.append("%20s - uninitialized variable" % ("self." + self._to_str(obj_key)))
  131. continue
  132. if obj_key.startswith("__"):
  133. continue
  134. if callable(obj_value):
  135. continue
  136. obj_value_str = self._to_str(obj_value, "%.200r")
  137. result.append("%20s = %s" % ("self." + self._to_str(obj_key), obj_value_str))
  138. result.append("</LOCALS>")
  139. s = u''
  140. for i in result:
  141. line = i.replace(r'\\n', '\n').replace(r'\n', '\n')
  142. if type(i) == unicode:
  143. s += i
  144. else:
  145. s += unicode(str(i), errors='replace')
  146. s += '\n'
  147. return s.encode('ascii', 'replace')
  148. def print_traceback(self):
  149. """Print a traceback string to stderr."""
  150. print >>sys.stderr, self.get_traceback()
  151. def _get_lines_from_file(self, filename, lineno, context_lines):
  152. # this function was taken from Django and adapted for CLI
  153. """
  154. Return context_lines before and after lineno from file.
  155. Return (pre_context_lineno, pre_context, context_line, post_context).
  156. """
  157. source = None
  158. try:
  159. f = open(filename)
  160. try:
  161. source = f.readlines()
  162. finally:
  163. f.close()
  164. except (OSError, IOError):
  165. pass
  166. if source is None:
  167. return None, [], None, []
  168. encoding = "ascii"
  169. for line in source[:2]:
  170. # File coding may be specified. Match pattern from PEP-263
  171. # (http://www.python.org/dev/peps/pep-0263/)
  172. match = re.search(r"coding[:=]\s*([-\w.]+)", line)
  173. if match:
  174. encoding = match.group(1)
  175. break
  176. source = [ unicode(sline, encoding, "replace") for sline in source ]
  177. lower_bound = max(0, lineno - context_lines)
  178. upper_bound = lineno + context_lines
  179. pre_context = [ line.strip("\n") for line in source[lower_bound:lineno] ]
  180. context_line = source[lineno].strip("\n")
  181. post_context = [ line.strip("\n") for line in source[lineno+1:upper_bound] ]
  182. return lower_bound, pre_context, context_line, post_context
  183. def get_frames(self):
  184. # this function was taken from Django and adapted for CLI
  185. """Return a list with frame information."""
  186. tb = self.exc_info[2]
  187. frames = []
  188. while tb is not None:
  189. # support for __traceback_hide__ which is used by a few libraries to hide internal frames.
  190. if tb.tb_frame.f_locals.get("__traceback_hide__"):
  191. tb = tb.tb_next
  192. continue
  193. filename = tb.tb_frame.f_code.co_filename
  194. function = tb.tb_frame.f_code.co_name
  195. lineno = tb.tb_lineno - 1
  196. pre_context_lineno, pre_context, context_line, post_context = self._get_lines_from_file(filename, lineno, 7)
  197. if pre_context_lineno is not None:
  198. frames.append({
  199. "tb": tb,
  200. "filename": filename,
  201. "function": function,
  202. "lineno": lineno + 1,
  203. "vars": tb.tb_frame.f_locals.items(),
  204. "id": id(tb),
  205. "pre_context": pre_context,
  206. "context_line": context_line,
  207. "post_context": post_context,
  208. "pre_context_lineno": pre_context_lineno + 1,
  209. })
  210. tb = tb.tb_next
  211. if not frames:
  212. frames = [{
  213. "filename": "?",
  214. "function": "?",
  215. "lineno": "?",
  216. }]
  217. return frames
  218. def set_except_hook(logger=None):
  219. """Replace standard excepthook method by an improved one."""
  220. def _hook(exctype, value, tb):
  221. tback = Traceback((exctype, value, tb))
  222. tback.show_locals = True
  223. logger and logger.error(tback.get_traceback())
  224. tback.print_traceback()
  225. print
  226. _hook.__doc__ = sys.excepthook.__doc__
  227. _hook.__name__ = sys.excepthook.__name__
  228. sys.excepthook = _hook