Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/stack_data/serializing.py: 30%

87 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1import inspect 

2import logging 

3import sys 

4import traceback 

5from collections import Counter 

6from html import escape as escape_html 

7from types import FrameType, TracebackType 

8from typing import Union, Iterable, List 

9 

10from stack_data import ( 

11 style_with_executing_node, 

12 Options, 

13 Line, 

14 FrameInfo, 

15 Variable, 

16 RepeatedFrames, 

17) 

18from stack_data.utils import some_str 

19 

20log = logging.getLogger(__name__) 

21 

22 

23class Serializer: 

24 def __init__( 

25 self, 

26 *, 

27 options=None, 

28 pygmented=False, 

29 show_executing_node=True, 

30 pygments_formatter_cls=None, 

31 pygments_formatter_kwargs=None, 

32 pygments_style="monokai", 

33 executing_node_modifier="bg:#005080", 

34 use_code_qualname=True, 

35 strip_leading_indent=True, 

36 html=False, 

37 chain=True, 

38 collapse_repeated_frames=True, 

39 show_variables=False, 

40 ): 

41 if options is None: 

42 options = Options() 

43 

44 if pygmented and not options.pygments_formatter: 

45 if show_executing_node: 

46 pygments_style = style_with_executing_node( 

47 pygments_style, executing_node_modifier 

48 ) 

49 

50 if pygments_formatter_cls is None: 

51 if html: 

52 from pygments.formatters.html import ( 

53 HtmlFormatter as pygments_formatter_cls, 

54 ) 

55 else: 

56 from pygments.formatters.terminal256 import ( 

57 Terminal256Formatter as pygments_formatter_cls, 

58 ) 

59 

60 options.pygments_formatter = pygments_formatter_cls( 

61 style=pygments_style, 

62 **pygments_formatter_kwargs or {}, 

63 ) 

64 

65 self.pygmented = pygmented 

66 self.use_code_qualname = use_code_qualname 

67 self.strip_leading_indent = strip_leading_indent 

68 self.html = html 

69 self.chain = chain 

70 self.options = options 

71 self.collapse_repeated_frames = collapse_repeated_frames 

72 self.show_variables = show_variables 

73 

74 def format_exception(self, e=None) -> List[dict]: 

75 if e is None: 

76 e = sys.exc_info()[1] 

77 

78 result = [] 

79 

80 if self.chain: 

81 if e.__cause__ is not None: 

82 result = self.format_exception(e.__cause__) 

83 result[-1]["tail"] = traceback._cause_message.strip() 

84 elif e.__context__ is not None and not e.__suppress_context__: 

85 result = self.format_exception(e.__context__) 

86 result[-1]["tail"] = traceback._context_message.strip() 

87 

88 result.append(self.format_traceback_part(e)) 

89 return result 

90 

91 def format_traceback_part(self, e: BaseException) -> dict: 

92 return dict( 

93 frames=self.format_stack(e.__traceback__ or sys.exc_info()[2]), 

94 exception=dict( 

95 type=type(e).__name__, 

96 message=some_str(e), 

97 ), 

98 tail="", 

99 ) 

100 

101 def format_stack(self, frame_or_tb=None) -> List[dict]: 

102 if frame_or_tb is None: 

103 frame_or_tb = inspect.currentframe().f_back 

104 

105 return list( 

106 self.format_stack_data( 

107 FrameInfo.stack_data( 

108 frame_or_tb, 

109 self.options, 

110 collapse_repeated_frames=self.collapse_repeated_frames, 

111 ) 

112 ) 

113 ) 

114 

115 def format_stack_data( 

116 self, stack: Iterable[Union[FrameInfo, RepeatedFrames]] 

117 ) -> Iterable[dict]: 

118 for item in stack: 

119 if isinstance(item, FrameInfo): 

120 if not self.should_include_frame(item): 

121 continue 

122 yield dict(type="frame", **self.format_frame(item)) 

123 else: 

124 yield dict(type="repeated_frames", **self.format_repeated_frames(item)) 

125 

126 def format_repeated_frames(self, repeated_frames: RepeatedFrames) -> dict: 

127 counts = sorted( 

128 Counter(repeated_frames.frame_keys).items(), 

129 key=lambda item: (-item[1], item[0][0].co_name), 

130 ) 

131 return dict( 

132 frames=[ 

133 dict( 

134 name=code.co_name, 

135 lineno=lineno, 

136 count=count, 

137 ) 

138 for (code, lineno), count in counts 

139 ] 

140 ) 

141 

142 def format_frame(self, frame: Union[FrameInfo, FrameType, TracebackType]) -> dict: 

143 if not isinstance(frame, FrameInfo): 

144 frame = FrameInfo(frame, self.options) 

145 

146 result = dict( 

147 name=( 

148 frame.executing.code_qualname() 

149 if self.use_code_qualname 

150 else frame.code.co_name 

151 ), 

152 filename=frame.filename, 

153 lineno=frame.lineno, 

154 lines=list(self.format_lines(frame.lines)), 

155 ) 

156 if self.show_variables: 

157 result["variables"] = list(self.format_variables(frame)) 

158 return result 

159 

160 def format_lines(self, lines): 

161 for line in lines: 

162 if isinstance(line, Line): 

163 yield dict(type="line", **self.format_line(line)) 

164 else: 

165 yield dict(type="line_gap") 

166 

167 def format_line(self, line: Line) -> dict: 

168 return dict( 

169 is_current=line.is_current, 

170 lineno=line.lineno, 

171 text=line.render( 

172 pygmented=self.pygmented, 

173 escape_html=self.html, 

174 strip_leading_indent=self.strip_leading_indent, 

175 ), 

176 ) 

177 

178 def format_variables(self, frame_info: FrameInfo) -> Iterable[dict]: 

179 try: 

180 for var in sorted(frame_info.variables, key=lambda v: v.name): 

181 yield self.format_variable(var) 

182 except Exception: # pragma: no cover 

183 log.exception("Error in getting frame variables") 

184 

185 def format_variable(self, var: Variable) -> dict: 

186 return dict( 

187 name=self.format_variable_part(var.name), 

188 value=self.format_variable_part(self.format_variable_value(var.value)), 

189 ) 

190 

191 def format_variable_part(self, text): 

192 if self.html: 

193 return escape_html(text) 

194 else: 

195 return text 

196 

197 def format_variable_value(self, value) -> str: 

198 return repr(value) 

199 

200 def should_include_frame(self, frame_info: FrameInfo) -> bool: 

201 return True # pragma: no cover