init python support
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -80,4 +80,9 @@ typings/
|
||||
.dynamodb/
|
||||
|
||||
dist/
|
||||
out/
|
||||
out/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
@@ -228,6 +228,16 @@ export type PlotlyVisualizationData = {
|
||||
text?: string | string[];
|
||||
xaxis?: string;
|
||||
yaxis?: string;
|
||||
cells?: {
|
||||
values?: string[][];
|
||||
};
|
||||
header?: {
|
||||
values?: string[];
|
||||
};
|
||||
domain?: {
|
||||
x?: number[],
|
||||
y?: number[],
|
||||
};
|
||||
x?: (string | number | null)[] | (string | number | null)[][];
|
||||
y?: (string | number | null)[] | (string | number | null)[][];
|
||||
z?: (string | number | null)[] | (string | number | null)[][];
|
||||
@@ -257,7 +267,8 @@ export type PlotlyVisualizationData = {
|
||||
| "waterfall"
|
||||
| "funnel"
|
||||
| "funnelarea"
|
||||
| "scattermapbox";
|
||||
| "scattermapbox"
|
||||
| "table";
|
||||
mode?:
|
||||
| "lines"
|
||||
| "markers"
|
||||
|
||||
7
demos/python/Person.py
Normal file
7
demos/python/Person.py
Normal file
@@ -0,0 +1,7 @@
|
||||
class Person:
|
||||
def __init__(self, name, parents=None) -> None:
|
||||
self.name = name
|
||||
self.parents = [] if parents is None else parents
|
||||
|
||||
def addParent(self, parent: "Person"):
|
||||
self.parents.append(parent)
|
||||
44
demos/python/debugvisualizer.py
Normal file
44
demos/python/debugvisualizer.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from Person import Person
|
||||
import json
|
||||
from vscodedebugvisualizer import globalVisualizationFactory
|
||||
|
||||
|
||||
class PersonVisualizer:
|
||||
def checkType(self, t):
|
||||
return isinstance(t, Person)
|
||||
|
||||
def visualizePerson(self, person: Person, nodes=[], edges=[]):
|
||||
if person.name in [n["id"] for n in nodes]:
|
||||
return nodes, edges
|
||||
|
||||
nodes.append(
|
||||
{
|
||||
"id": person.name,
|
||||
"label": person.name,
|
||||
}
|
||||
)
|
||||
|
||||
for p in person.parents:
|
||||
nodes, edges = self.visualizePerson(p, nodes, edges)
|
||||
edges.append(
|
||||
{
|
||||
"from": p.name,
|
||||
"to": person.name,
|
||||
}
|
||||
)
|
||||
|
||||
return nodes, edges
|
||||
|
||||
def visualize(self, person: Person):
|
||||
jsonDict = {
|
||||
"kind": {"graph": True},
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
}
|
||||
|
||||
self.visualizePerson(person, jsonDict["nodes"], jsonDict["edges"])
|
||||
|
||||
return json.dumps(jsonDict)
|
||||
|
||||
|
||||
globalVisualizationFactory.addVisualizer(PersonVisualizer())
|
||||
123
demos/python/demo.py
Normal file
123
demos/python/demo.py
Normal file
@@ -0,0 +1,123 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
# put "x" in the Debug Visualizer and use step by step
|
||||
# debugging to see the diffrent types of data visualization
|
||||
|
||||
|
||||
x = 5
|
||||
x = "asdf"
|
||||
x = 5.5
|
||||
x = [1, 2, 3, 4, 5, 6, "a", "b"]
|
||||
x = ["b", "a", "c", "d", "e"]
|
||||
x.sort()
|
||||
x = {
|
||||
"asdf1": "dasdf",
|
||||
"asdf2": "dasdf",
|
||||
"asdf3": {"foo": "bar"},
|
||||
}
|
||||
|
||||
x = [1, 2, 3, 4, 5, 6]
|
||||
# histogram
|
||||
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||
x = x.reshape(2, -1)
|
||||
|
||||
|
||||
# one dimension
|
||||
x = np.arange(100)
|
||||
x = np.linspace(-np.pi, np.pi, 100)
|
||||
x = np.sin(x)
|
||||
|
||||
# 2 dimension
|
||||
x = x.reshape(5, 20)
|
||||
# 2 dimension transpose
|
||||
x = x.transpose()
|
||||
x = x.transpose()
|
||||
|
||||
# 3 dimensions
|
||||
x = x.reshape(2, 5, 10)
|
||||
|
||||
# 4 dimensions
|
||||
x = x.reshape(2, 5, 2, 5)
|
||||
|
||||
# big number
|
||||
x = np.empty(2 ** 24)
|
||||
x[0:1000000] = 1
|
||||
x[1000000:10000000] = 5
|
||||
|
||||
# pyTorch
|
||||
try:
|
||||
import torch
|
||||
|
||||
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||
x = x.reshape(2, -1)
|
||||
x = torch.Tensor(x)
|
||||
pass
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# tensorflow
|
||||
|
||||
try:
|
||||
import tensorflow as tf
|
||||
|
||||
x = np.concatenate([np.arange(i) for i in range(9)])
|
||||
x = x.reshape(2, -1)
|
||||
x = tf.convert_to_tensor(x)
|
||||
pass
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# pandas
|
||||
|
||||
try:
|
||||
import pandas as pd
|
||||
import plotly.express as px
|
||||
|
||||
x = px.data.gapminder().query("year == 2007")
|
||||
pass
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# custom visualizer defined in ./debugvisualizer.py (this file is included automatically)
|
||||
|
||||
from Person import Person
|
||||
|
||||
x = Person("Aria")
|
||||
parent1 = Person("Eduart")
|
||||
parent2 = Person("Catelyn")
|
||||
x.addParent(parent1)
|
||||
x.addParent(parent2)
|
||||
parent1.addParent(Person("Benjen"))
|
||||
|
||||
# direct debug-visualizer json as dict with property "kind"
|
||||
|
||||
x = {
|
||||
"kind": {"dotGraph": True},
|
||||
"text": '\ndigraph G {\n subgraph cluster_0 {\n style=filled;\n color=lightgrey;\n node [style=filled,color=white];\n a0 -> a1 -> a2 -> a3;\n label = "process #1";\n }\n \n subgraph cluster_1 {\n node [style=filled];\n b0 -> b1 -> b2 -> b3;\n label = "process #2";\n color=blue\n }\n start -> a0;\n start -> b0;\n a1 -> b3;\n b2 -> a3;\n a3 -> a0;\n a3 -> end;\n b3 -> end;\n \n start [shape=Mdiamond];\n end [shape=Msquare];\n}\n',
|
||||
}
|
||||
|
||||
x = {
|
||||
"kind": {"plotly": True},
|
||||
"data": [
|
||||
{
|
||||
"mode": "lines",
|
||||
"type": "scatter",
|
||||
"x": ["A", "B", "C"],
|
||||
"xaxis": "x",
|
||||
"y": [4488916, 3918072, 3892124],
|
||||
"yaxis": "y",
|
||||
},
|
||||
{
|
||||
"cells": {"values": [["A", "B", "C"], [341319, 281489, 294786], [4488916, 3918072, 3892124]]},
|
||||
"domain": {"x": [0.0, 1.0], "y": [0.0, 0.60]},
|
||||
"header": {"align": "left", "font": {"size": 10}, "values": ["Date", "Number", "Output"]},
|
||||
"type": "table",
|
||||
},
|
||||
],
|
||||
"layout": {"xaxis": {"anchor": "y", "domain": [0.0, 1.0]}, "yaxis": {"anchor": "x", "domain": [0.75, 1.0]}},
|
||||
}
|
||||
pass
|
||||
@@ -23,10 +23,9 @@ for i in range(2,100):
|
||||
# connects the node to a random edge
|
||||
targetId = str(randint(1, i - 1))
|
||||
graph["edges"].append({"from": id, "to": targetId})
|
||||
json_graph = dumps(graph)
|
||||
print("i is " + str(i))
|
||||
# try setting a breakpoint right above
|
||||
# then put json_graph into the visualization console and press enter
|
||||
# then put graph into the visualization console and press enter
|
||||
# when you step through the code each time you hit the breakpoint
|
||||
# the graph should automatically refresh!
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Python demo for sorting using VS Code Debug Visualizer."""
|
||||
import json
|
||||
|
||||
|
||||
def serialize(arr):
|
||||
@@ -14,7 +13,7 @@ def serialize(arr):
|
||||
}
|
||||
],
|
||||
}
|
||||
return json.dumps(formatted)
|
||||
return formatted
|
||||
|
||||
|
||||
arr = [6, 9, 3, 12, 1, 11, 5, 13, 8, 14, 2, 4, 10, 0, 7]
|
||||
|
||||
151
extension/src/VisualizationBackend/PyVisualizationSupport.ts
Normal file
151
extension/src/VisualizationBackend/PyVisualizationSupport.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
GraphNode,
|
||||
GraphVisualizationData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import {
|
||||
DebugSessionVisualizationSupport,
|
||||
VisualizationBackend,
|
||||
GetVisualizationDataArgs,
|
||||
VisualizationBackendBase,
|
||||
} from "./VisualizationBackend";
|
||||
import { Config } from "../Config";
|
||||
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class PyEvaluationEngine
|
||||
implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) { }
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
|
||||
const supportedDebugAdapters = [
|
||||
"python",
|
||||
];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new PyVisualizationBackend(
|
||||
session,
|
||||
this.debuggerView,
|
||||
this.config
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class PyVisualizationBackend extends VisualizationBackendBase {
|
||||
public readonly expressionLanguageId = "python";
|
||||
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {
|
||||
super(debugSession, debuggerView);
|
||||
}
|
||||
|
||||
protected getContext(): "watch" | "repl" {
|
||||
// we will use "repl" as default so that results are not truncated.
|
||||
return "repl";
|
||||
}
|
||||
|
||||
public async getVisualizationData({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
}: GetVisualizationDataArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||
this.debugSession
|
||||
);
|
||||
|
||||
const finalExpression = this.getFinalExpression({
|
||||
expression,
|
||||
preferredExtractorId,
|
||||
});
|
||||
let reply: { result: string; variablesReference: number };
|
||||
try {
|
||||
// inject vscodedebugvisualizer for python
|
||||
await this.debugSession.evaluate({
|
||||
expression: 'from vscodedebugvisualizer import visualize\ntry:\n import debugvisualizer\nexcept ImportError:\n pass',
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
reply = await this.debugSession.evaluate({
|
||||
expression: finalExpression,
|
||||
frameId,
|
||||
context: this.getContext(),
|
||||
});
|
||||
|
||||
let result = reply.result;
|
||||
// remove the initial escape by the the debug session e.g. `''{"kind": {"text": true}, "text": "{"asdf1\'"}''`
|
||||
result = result.replaceAll(/\\'/g, "'");
|
||||
result = result.replaceAll(/\\'/g, "'");
|
||||
result = result.replaceAll(/\\\\/g, "\\")
|
||||
result.substr(1, result.length - 2);
|
||||
|
||||
return parseEvaluationResultFromGenericDebugAdapter(
|
||||
result,
|
||||
{
|
||||
debugAdapterType: this.debugSession.session
|
||||
.configuration.type,
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
let errorTyped = error as Error;
|
||||
if (errorTyped.message.includes("ModuleNotFoundError: No module named 'vscodedebugvisualizer'")) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: ["Pleas make sure vscodedebugvisualizer is installed: `pip install vscodedebugvisualizer`"],
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: [
|
||||
"An error occurred while evaluating the expression:",
|
||||
errorTyped.message,
|
||||
`Used debug adapter: ${this.debugSession.session.configuration.type}`,
|
||||
{
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"Evaluated expression is",
|
||||
{ kind: "code", content: finalExpression },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected getFinalExpression(args: {
|
||||
expression: string;
|
||||
preferredExtractorId: DataExtractorId | undefined;
|
||||
}): string {
|
||||
// wrap expression with visualize function
|
||||
let pythonInject = "";
|
||||
pythonInject += "visualize(" + args.expression + ")";
|
||||
return pythonInject;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,5 +2,6 @@ export * from "./VisualizationBackend";
|
||||
|
||||
export { ComposedVisualizationSupport } from "./ComposedVisualizationSupport";
|
||||
export { JsEvaluationEngine } from "./JsVisualizationSupport";
|
||||
export { PyEvaluationEngine } from "./PyVisualizationSupport";
|
||||
export { GenericVisualizationSupport } from "./GenericVisualizationSupport";
|
||||
export { ConfigurableVisualizationSupport } from "./ConfigurableVisualizationSupport";
|
||||
|
||||
@@ -21,6 +21,7 @@ import { VisualizationWatchModelImpl } from "./VisualizationWatchModel";
|
||||
import {
|
||||
ComposedVisualizationSupport,
|
||||
JsEvaluationEngine,
|
||||
PyEvaluationEngine,
|
||||
GenericVisualizationSupport,
|
||||
ConfigurableVisualizationSupport,
|
||||
} from "./VisualizationBackend";
|
||||
@@ -32,7 +33,7 @@ export function activate(context: ExtensionContext) {
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
export function deactivate() { }
|
||||
|
||||
export class Extension {
|
||||
public readonly dispose = Disposable.fn();
|
||||
@@ -51,6 +52,7 @@ export class Extension {
|
||||
this.debuggerView
|
||||
),
|
||||
new JsEvaluationEngine(this.debuggerView, this.config),
|
||||
new PyEvaluationEngine(this.debuggerView, this.config),
|
||||
new GenericVisualizationSupport(this.debuggerView),
|
||||
]),
|
||||
this.debuggerView
|
||||
|
||||
Reference in New Issue
Block a user