print_exc_plus.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import os
  2. import re
  3. import sys
  4. import traceback
  5. from itertools import islice
  6. from types import FrameType
  7. from typing import Sized, Dict, Tuple
  8. from lib.threading_timer_decorator import exit_after
  9. try:
  10. import numpy
  11. except ImportError:
  12. numpy = None
  13. FORMATTING_OPTIONS = {
  14. 'MAX_LINE_LENGTH': 1024,
  15. 'SHORT_LINE_THRESHOLD': 128,
  16. 'MAX_NEWLINES': 20,
  17. }
  18. ID = int
  19. # noinspection PyPep8Naming
  20. def name_or_str(X):
  21. try:
  22. return re.search(r"<class '?(.*?)'?>", str(X))[1]
  23. except TypeError: # if not found
  24. return str(X)
  25. @exit_after(2)
  26. def type_string(x):
  27. if numpy is not None and isinstance(x, numpy.ndarray):
  28. return name_or_str(type(x)) + str(x.shape)
  29. elif isinstance(x, Sized):
  30. return name_or_str(type(x)) + f'({len(x)})'
  31. else:
  32. return name_or_str(type(x))
  33. @exit_after(2)
  34. def to_string_with_timeout(x):
  35. return str(x)
  36. def nth_index(iterable, value, n):
  37. matches = (idx for idx, val in enumerate(iterable) if val == value)
  38. return next(islice(matches, n - 1, n), None)
  39. def print_exc_plus():
  40. """
  41. Print the usual traceback information, followed by a listing of all the
  42. local variables in each frame.
  43. """
  44. limit = FORMATTING_OPTIONS['MAX_LINE_LENGTH']
  45. max_newlines = FORMATTING_OPTIONS['MAX_NEWLINES']
  46. tb = sys.exc_info()[2]
  47. if numpy is not None:
  48. options = numpy.get_printoptions()
  49. numpy.set_printoptions(precision=2, edgeitems=2, floatmode='maxprec', threshold=20, linewidth=120)
  50. else:
  51. options = {}
  52. stack = []
  53. long_printed_objs: Dict[ID, Tuple[str, FrameType]] = {}
  54. while tb:
  55. stack.append(tb.tb_frame)
  56. tb = tb.tb_next
  57. for frame in stack:
  58. if frame is not stack[0]:
  59. print('-' * 40)
  60. try:
  61. print("Frame %s in %s at line %s" % (frame.f_code.co_name,
  62. os.path.relpath(frame.f_code.co_filename),
  63. frame.f_lineno))
  64. except ValueError: # if path is not relative
  65. print("Frame %s in %s at line %s" % (frame.f_code.co_name,
  66. frame.f_code.co_filename,
  67. frame.f_lineno))
  68. for key, value in frame.f_locals.items():
  69. # We have to be careful not to cause a new error in our error
  70. # printer! Calling str() on an unknown object could cause an
  71. # error we don't want.
  72. # noinspection PyBroadException
  73. try:
  74. key_string = to_string_with_timeout(key)
  75. except KeyboardInterrupt:
  76. key_string = "<TIMEOUT WHILE PRINTING KEY>"
  77. except Exception:
  78. key_string = "<ERROR WHILE PRINTING KEY>"
  79. # noinspection PyBroadException
  80. try:
  81. type_as_string = type_string(value)
  82. except KeyboardInterrupt:
  83. type_as_string = "<TIMEOUT WHILE PRINTING TYPE>"
  84. except Exception as e:
  85. # noinspection PyBroadException
  86. try:
  87. type_as_string = f"<{type(e).__name__} WHILE PRINTING TYPE>"
  88. except Exception:
  89. type_as_string = "<ERROR WHILE PRINTING TYPE>"
  90. if id(value) in long_printed_objs:
  91. prev_key_string, prev_frame = long_printed_objs[id(value)]
  92. if prev_frame is frame:
  93. print("\t%s is the same as '%s'" %
  94. (key_string + ' : ' + type_as_string,
  95. prev_key_string))
  96. else:
  97. print("\t%s is the same as '%s' in frame %s in %s at line %s." %
  98. (key_string + ' : ' + type_as_string,
  99. prev_key_string,
  100. prev_frame.f_code.co_name,
  101. os.path.relpath(prev_frame.f_code.co_filename),
  102. prev_frame.f_lineno))
  103. continue
  104. # noinspection PyBroadException
  105. try:
  106. value_string = to_string_with_timeout(value)
  107. except KeyboardInterrupt:
  108. value_string = "<TIMEOUT WHILE PRINTING VALUE>"
  109. except Exception:
  110. value_string = "<ERROR WHILE PRINTING VALUE>"
  111. line: str = '\t' + key_string + ' : ' + type_as_string + ' = ' + value_string
  112. if limit is not None and len(line) > limit:
  113. line = line[:limit - 1] + '...'
  114. if max_newlines is not None and line.count('\n') > max_newlines:
  115. line = line[:nth_index(line, '\n', max_newlines)].strip() + '... (' + str(
  116. line[nth_index(line, '\n', max_newlines):].count('\n')) + ' more lines)'
  117. if len(line) > FORMATTING_OPTIONS['SHORT_LINE_THRESHOLD']:
  118. long_printed_objs[id(value)] = key_string, frame
  119. print(line)
  120. traceback.print_exc()
  121. if numpy is not None:
  122. numpy.set_printoptions(**options)
  123. def main():
  124. def fun1(c, d, e):
  125. return fun2(c, d + e)
  126. def fun2(g, h):
  127. raise RuntimeError
  128. def fun3(z):
  129. return numpy.zeros(shape=z)
  130. try:
  131. import numpy as np
  132. fun1(numpy.random.normal(size=(3, 4, 5, 6)), '12321', '123')
  133. data = '???' * 100
  134. fun3(data)
  135. except:
  136. print_exc_plus()
  137. if __name__ == '__main__':
  138. main()