Skip to content

Commit bcca103

Browse files
authored
Merge pull request #166 from skogsbaer/show-exceptions
Show traceback of exception in output area
2 parents d29f42a + 186820c commit bcca103

File tree

5 files changed

+54
-32
lines changed

5 files changed

+54
-32
lines changed

media/programflow-visualization/webview.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,7 @@ body {
188188
.return-value {
189189
color: blue;
190190
}
191+
192+
.traceback-text {
193+
color: red;
194+
}

pytrace-generator/main.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import re
1111
import socket
1212
import sys
13+
import traceback
1314
import types
1415
import typing
1516

@@ -253,15 +254,19 @@ class TraceStep:
253254
stack: Stack
254255
heap: Heap
255256
stdout: str
257+
traceback_text: str
256258

257259
def format(self):
258-
return {
260+
step = {
259261
"line": self.line,
260262
"filePath": self.file_path,
261263
"stack": self.stack.format(),
262264
"heap": self.heap.format(),
263265
"stdout": self.stdout,
264266
}
267+
if self.traceback_text is not None:
268+
step["traceback"] = self.traceback_text
269+
return step
265270

266271

267272
def should_ignore(variable_name, value, script_path, ignore_list = []):
@@ -377,7 +382,8 @@ def trace_dispatch(self, frame, event, arg):
377382
self.import_following = import_regex.search(next_source_line) is not None
378383

379384
display_return = event == "return" and self.last_event != "exception" and len(self.stack.frames) > 1
380-
if event == "line" or display_return:
385+
display_exception = event == "exception" and self.last_event != "return"
386+
if event == "line" or display_return or display_exception:
381387
for variable_name in frame.f_locals:
382388
if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], self.filename, self.stack_ignore):
383389
continue
@@ -389,7 +395,15 @@ def trace_dispatch(self, frame, event, arg):
389395
heap = generate_heap(frame, self.filename, self.stack_ignore)
390396
accumulated_stdout = self.accumulated_stdout + self.captured_stdout.getvalue()
391397

392-
step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap), accumulated_stdout)
398+
traceback_text = None
399+
if event == "exception":
400+
exception_value = arg[1]
401+
traceback_text_tmp = io.StringIO()
402+
traceback.print_exception(exception_value, limit=0, file=traceback_text_tmp)
403+
traceback_text = traceback_text_tmp.getvalue()
404+
405+
406+
step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap), accumulated_stdout, traceback_text)
393407

394408
is_annotation = next_source_line.startswith("@")
395409
should_display_step = not is_annotation
@@ -412,6 +426,10 @@ def trace_dispatch(self, frame, event, arg):
412426
self.shown_class_defs.append(filename, line)
413427
self.last_step_was_class = is_class_def
414428
self.prev_num_frames = num_frames
429+
430+
if event == "exception":
431+
# Terminate visualization after first exception in user code
432+
self.set_quit()
415433
if event == "call":
416434
self.stack.push_frame(frame)
417435
self.shown_class_defs.push_frame()

pytrace-generator/test/test-cases/divideByZero.py.json

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@
276276
"stdout": ""
277277
},
278278
{
279-
"line": 11,
279+
"line": 3,
280280
"filePath": "divideByZero.py",
281281
"stack": [
282282
{
@@ -288,45 +288,40 @@
288288
"name": "bar"
289289
}
290290
]
291-
}
292-
],
293-
"heap": {},
294-
"stdout": ""
295-
},
296-
{
297-
"line": 12,
298-
"filePath": "divideByZero.py",
299-
"stack": [
291+
},
300292
{
301-
"frameName": "<module>",
293+
"frameName": "bar",
302294
"locals": [
303295
{
304-
"type": "function",
305-
"value": "<function bar>",
306-
"name": "bar"
296+
"type": "int",
297+
"value": 2,
298+
"name": "i"
307299
}
308300
]
309-
}
310-
],
311-
"heap": {},
312-
"stdout": ""
313-
},
314-
{
315-
"line": 14,
316-
"filePath": "divideByZero.py",
317-
"stack": [
301+
},
318302
{
319-
"frameName": "<module>",
303+
"frameName": "bar",
320304
"locals": [
321305
{
322-
"type": "function",
323-
"value": "<function bar>",
324-
"name": "bar"
306+
"type": "int",
307+
"value": 1,
308+
"name": "i"
309+
}
310+
]
311+
},
312+
{
313+
"frameName": "bar",
314+
"locals": [
315+
{
316+
"type": "int",
317+
"value": 0,
318+
"name": "i"
325319
}
326320
]
327321
}
328322
],
329323
"heap": {},
330-
"stdout": ""
324+
"stdout": "",
325+
"traceback": "ZeroDivisionError: division by zero\n"
331326
}
332327
]

src/programflow-visualization/frontend/HTMLGenerator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ export class HTMLGenerator {
4646
${keys.map((name, index) => this.objectItem(name, values[index])).join('')}
4747
<div>
4848
`;
49-
return [traceElement.line, frameItems, objectItems, traceElement.filePath, traceElement.stdout];
49+
let output = traceElement.stdout;
50+
if (traceElement.traceback !== undefined) {
51+
output += `<span class="traceback-text">${traceElement.traceback}</span>`;
52+
}
53+
return [traceElement.line, frameItems, objectItems, traceElement.filePath, output];
5054
}
5155

5256
private objectItem(name: string, value: HeapValue): string {

src/programflow-visualization/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type BackendTraceElem = {
2121
stack: Array<StackElem>;
2222
heap: Map<Address, HeapValue>;
2323
stdout: string;
24+
traceback: string | undefined;
2425
};
2526

2627
type Address = number;

0 commit comments

Comments
 (0)