Skip to content

Commit

Permalink
Show traceback of exception in output area
Browse files Browse the repository at this point in the history
Also terminate the visualization on the first exception in user code.
  • Loading branch information
hannesbraun committed Dec 3, 2024
1 parent ae67cfa commit 877f05e
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 32 deletions.
4 changes: 4 additions & 0 deletions media/programflow-visualization/webview.css
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,7 @@ body {
.return-value {
color: blue;
}

.traceback-text {
color: red;
}
24 changes: 21 additions & 3 deletions pytrace-generator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import re
import socket
import sys
import traceback
import types

def eprint(*args, **kwargs):
Expand Down Expand Up @@ -249,15 +250,19 @@ class TraceStep:
stack: Stack
heap: Heap
stdout: str
traceback_text: str

def format(self):
return {
step = {
"line": self.line,
"filePath": self.file_path,
"stack": self.stack.format(),
"heap": self.heap.format(),
"stdout": self.stdout,
}
if self.traceback_text is not None:
step["traceback"] = self.traceback_text
return step


def should_ignore(variable_name, value, script_path, ignore_list = []):
Expand Down Expand Up @@ -373,7 +378,8 @@ def trace_dispatch(self, frame, event, arg):
self.import_following = import_regex.search(next_source_line) is not None

display_return = event == "return" and self.last_event != "exception" and len(self.stack.frames) > 1
if event == "line" or display_return:
display_exception = event == "exception" and self.last_event != "return"
if event == "line" or display_return or display_exception:
for variable_name in frame.f_locals:
if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], self.filename, self.stack_ignore):
continue
Expand All @@ -385,7 +391,15 @@ def trace_dispatch(self, frame, event, arg):
heap = generate_heap(frame, self.filename, self.stack_ignore)
accumulated_stdout = self.accumulated_stdout + self.captured_stdout.getvalue()

step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap), accumulated_stdout)
traceback_text = None
if event == "exception":
exception_value = arg[1]
traceback_text_tmp = io.StringIO()
traceback.print_exception(exception_value, file=traceback_text_tmp)
traceback_text = traceback_text_tmp.getvalue()


step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap), accumulated_stdout, traceback_text)

is_annotation = next_source_line.startswith("@")
should_display_step = not is_annotation
Expand All @@ -408,6 +422,10 @@ def trace_dispatch(self, frame, event, arg):
self.shown_class_defs.append(filename, line)
self.last_step_was_class = is_class_def
self.prev_num_frames = num_frames

if event == "exception":
# Terminate visualization after first exception in user code
self.set_quit()
if event == "call":
self.stack.push_frame(frame)
self.shown_class_defs.push_frame()
Expand Down
51 changes: 23 additions & 28 deletions pytrace-generator/test/test-cases/divideByZero.py.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@
"stdout": ""
},
{
"line": 11,
"line": 3,
"filePath": "divideByZero.py",
"stack": [
{
Expand All @@ -288,45 +288,40 @@
"name": "bar"
}
]
}
],
"heap": {},
"stdout": ""
},
{
"line": 12,
"filePath": "divideByZero.py",
"stack": [
},
{
"frameName": "<module>",
"frameName": "bar",
"locals": [
{
"type": "function",
"value": "<function bar>",
"name": "bar"
"type": "int",
"value": 2,
"name": "i"
}
]
}
],
"heap": {},
"stdout": ""
},
{
"line": 14,
"filePath": "divideByZero.py",
"stack": [
},
{
"frameName": "<module>",
"frameName": "bar",
"locals": [
{
"type": "function",
"value": "<function bar>",
"name": "bar"
"type": "int",
"value": 1,
"name": "i"
}
]
},
{
"frameName": "bar",
"locals": [
{
"type": "int",
"value": 0,
"name": "i"
}
]
}
],
"heap": {},
"stdout": ""
"stdout": "",
"traceback": "Traceback (most recent call last):\n File \"/Users/hannesbraun/Documents/Development/write-your-python-program/pytrace-generator/test/test-cases/divideByZero.py\", line 3, in bar\n return 1/0\n ~^~\nZeroDivisionError: division by zero\n"
}
]
6 changes: 5 additions & 1 deletion src/programflow-visualization/frontend/HTMLGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export class HTMLGenerator {
${keys.map((name, index) => this.objectItem(name, values[index])).join('')}
<div>
`;
return [traceElement.line, frameItems, objectItems, traceElement.filePath, traceElement.stdout];
let output = traceElement.stdout;
if (traceElement.traceback !== undefined) {
output += `<span class="traceback-text">${traceElement.traceback}</span>`;
}
return [traceElement.line, frameItems, objectItems, traceElement.filePath, output];
}

private objectItem(name: string, value: HeapValue): string {
Expand Down
1 change: 1 addition & 0 deletions src/programflow-visualization/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type BackendTraceElem = {
stack: Array<StackElem>;
heap: Map<Address, HeapValue>;
stdout: string;
traceback: string | undefined;
};

type Address = number;
Expand Down

0 comments on commit 877f05e

Please sign in to comment.