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
« 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
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
20log = logging.getLogger(__name__)
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()
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 )
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 )
60 options.pygments_formatter = pygments_formatter_cls(
61 style=pygments_style,
62 **pygments_formatter_kwargs or {},
63 )
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
74 def format_exception(self, e=None) -> List[dict]:
75 if e is None:
76 e = sys.exc_info()[1]
78 result = []
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()
88 result.append(self.format_traceback_part(e))
89 return result
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 )
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
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 )
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))
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 )
142 def format_frame(self, frame: Union[FrameInfo, FrameType, TracebackType]) -> dict:
143 if not isinstance(frame, FrameInfo):
144 frame = FrameInfo(frame, self.options)
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
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")
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 )
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")
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 )
191 def format_variable_part(self, text):
192 if self.html:
193 return escape_html(text)
194 else:
195 return text
197 def format_variable_value(self, value) -> str:
198 return repr(value)
200 def should_include_frame(self, frame_info: FrameInfo) -> bool:
201 return True # pragma: no cover