Coverage for src\zapy\templating\traceback.py: 60%
47 statements
« prev ^ index » next coverage.py v7.3.4, created at 2023-12-20 14:17 -0500
« prev ^ index » next coverage.py v7.3.4, created at 2023-12-20 14:17 -0500
1import sys
2import traceback
3import textwrap
5from zapy.utils.singleton import SingletonMeta
7from typing_extensions import TypedDict
10class TracebackInfo(TypedDict):
11 exception_type: str
12 exception_message: str
13 line: int
14 stacktrace: str
17def annotate_traceback(exc_obj: BaseException, script="", location=""):
18 traceback_info = TracebackHandler().extract(script, location)
19 exc_obj._parse_errors = traceback_info
20 exc_obj.add_note(traceback_info['stacktrace'])
21 return traceback_info
23def recover_traceback(exc_obj: BaseException):
24 return getattr(exc_obj, "_parse_errors", None)
26def copy_traceback(exc_obj: BaseException, from_exc: BaseException):
27 info = recover_traceback(from_exc)
28 if info:
29 exc_obj._parse_errors = info
30 exc_obj.add_note(info["stacktrace"])
31 return info
34class TracebackHandler(metaclass=SingletonMeta):
35 header = "Traceback (most recent call last):"
37 def extract(self, script="", location="") -> TracebackInfo:
38 exc_type, exc_obj, exc_tb = sys.exc_info()
39 frame_summary = traceback.extract_tb(exc_tb)[-1]
40 if frame_summary.filename == "<string>": 40 ↛ 41line 40 didn't jump to line 41, because the condition on line 40 was never true
41 return self.extract_from_frame(frame_summary, exc_obj, script=script, location=location)
42 else:
43 return self.extract_from_error(exc_obj, location=location)
45 def extract_from_frame(self, frame_summary: traceback.FrameSummary, exc_obj, script='', location=''):
46 stacktrace = textwrap.dedent(f"""\
47 {self.header}
48 {location}, line {frame_summary.lineno}, in {frame_summary.name}
49 {frame_summary.line or self.__extract_line(script, frame_summary.lineno)}
50 {exc_obj.__class__.__name__}: {exc_obj}""")
51 return {
52 "exception_type": exc_obj.__class__.__name__,
53 "exception_message": str(exc_obj),
54 "line": frame_summary.lineno,
55 "stacktrace": stacktrace,
56 }
58 def extract_from_error(self, exc_obj, location=''):
59 tracelines = traceback.format_exception_only(exc_obj)
60 tracelines[0] = tracelines[0].replace('File "<string>"', location)
61 tracelines.insert(0, self.header + '\n')
62 return {
63 "exception_type": exc_obj.__class__.__name__,
64 "exception_message": str(exc_obj),
65 "line": exc_obj.lineno if isinstance(exc_obj, SyntaxError) else None,
66 "stacktrace": "".join(tracelines),
67 }
69 def __extract_line(self, script: str, line: int):
70 if not script:
71 return ''
72 try:
73 script_lines = script.split('\n')
74 return script_lines[line - 1].strip()
75 except Exception:
76 return "<<Zapy: line omitted due to an error on extraction, check your line separation>>"