Skip to content

Commit d4269ce

Browse files
committed
Merge branch 'master' of github.com:skogsbaer/write-your-python-program
2 parents c723958 + bcca103 commit d4269ce

File tree

7 files changed

+66
-33
lines changed

7 files changed

+66
-33
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: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import re
1111
import socket
1212
import sys
13+
import traceback
1314
import types
15+
import typing
1416

1517
def eprint(*args, **kwargs):
1618
print(*args, file=sys.stderr, **kwargs)
@@ -29,6 +31,7 @@ def eprint(*args, **kwargs):
2931
str: "str",
3032
type(None): "none",
3133
type: "type",
34+
typing.TypeAliasType: "type",
3235
types.FunctionType: "function"
3336
}
3437
HEAP_TYPES = {
@@ -120,6 +123,8 @@ def format(self):
120123
if search_result is not None:
121124
type_name = f"<class '{search_result.group(1)}'>"
122125
d["value"] = type_name
126+
elif type(d["value"]) == typing.TypeAliasType:
127+
d["value"] = "<TypeAlias>"
123128
elif inspect.isfunction(d["value"]):
124129
function_desc = str(d["value"])
125130
search_result = function_str_regex.search(function_desc)
@@ -249,15 +254,19 @@ class TraceStep:
249254
stack: Stack
250255
heap: Heap
251256
stdout: str
257+
traceback_text: str
252258

253259
def format(self):
254-
return {
260+
step = {
255261
"line": self.line,
256262
"filePath": self.file_path,
257263
"stack": self.stack.format(),
258264
"heap": self.heap.format(),
259265
"stdout": self.stdout,
260266
}
267+
if self.traceback_text is not None:
268+
step["traceback"] = self.traceback_text
269+
return step
261270

262271

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

375384
display_return = event == "return" and self.last_event != "exception" and len(self.stack.frames) > 1
376-
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:
377387
for variable_name in frame.f_locals:
378388
if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], self.filename, self.stack_ignore):
379389
continue
@@ -385,7 +395,15 @@ def trace_dispatch(self, frame, event, arg):
385395
heap = generate_heap(frame, self.filename, self.stack_ignore)
386396
accumulated_stdout = self.accumulated_stdout + self.captured_stdout.getvalue()
387397

388-
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)
389407

390408
is_annotation = next_source_line.startswith("@")
391409
should_display_step = not is_annotation
@@ -408,6 +426,10 @@ def trace_dispatch(self, frame, event, arg):
408426
self.shown_class_defs.append(filename, line)
409427
self.last_step_was_class = is_class_def
410428
self.prev_num_frames = num_frames
429+
430+
if event == "exception":
431+
# Terminate visualization after first exception in user code
432+
self.set_quit()
411433
if event == "call":
412434
self.stack.push_frame(frame)
413435
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/frontend/frontend.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { VisualizationPanel } from './visualization_panel';
33
import { MessagePort } from 'worker_threads';
44
import * as TraceCache from '../trace_cache';
55

6+
let panel: VisualizationPanel | undefined = undefined;
7+
68
export async function startFrontend(
79
context: ExtensionContext,
810
filePath: string,
@@ -13,7 +15,8 @@ export async function startFrontend(
1315
trace = await TraceCache.getTrace(context, fileHash);
1416
}
1517

16-
const panel = await VisualizationPanel.getVisualizationPanel(context, filePath, fileHash, trace, tracePort);
18+
panel?.dispose();
19+
panel = await VisualizationPanel.getVisualizationPanel(context, filePath, fileHash, trace, tracePort);
1720
if (!panel) {
1821
return failure("Frontend couldn't be initialized!");
1922
}

src/programflow-visualization/frontend/visualization_panel.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ export class VisualizationPanel {
112112
});
113113
}
114114

115+
public dispose() {
116+
this._panel?.dispose();
117+
}
118+
115119
public static async getVisualizationPanel(
116120
context: vscode.ExtensionContext,
117121
filePath: 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)