-
-
Notifications
You must be signed in to change notification settings - Fork 417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added skeleton of package for python support #81
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import pyDataExtraction.commonTypes |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import json | ||
from typing import Union, Dict, Optional | ||
from abc import ABC | ||
|
||
from pyDataExtraction.commonTypes.Graph import Graph | ||
|
||
if __name__ == "__main__": | ||
# NOTE this will likely be what will be called by the PyEvaluation Engine, | ||
# this will be where this library interfaces with the existing codebase | ||
|
||
# TODO: implement testing for each dataType, it may be a good idea to compare | ||
# with output of typescript extractors | ||
graph_data1 = {"A": ["B", "C"], "B": ["A", "C"], "C": ["A", "D"], "D": ["A"]} | ||
graph_data2 = {1: [2, 3], 2: [1, 3], 3: [1, 4], 4: [1]} | ||
graph = Graph(graph_data1) | ||
print(graph) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use logging with respective log level There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what you mean. I was mainly using I'm not sure if we could use the same test directory as node without pytest throwing errors due to the presence of js files. my experience with pytest and how it works is limited. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
from typing import Union, Dict, Optional | ||
from pyDataExtraction.commonTypes.base import DataType | ||
|
||
# from docs | ||
""" | ||
interface NodeGraphData { | ||
id: string; | ||
label?: string; | ||
color?: string; | ||
shape?: "ellipse" | "box"; | ||
} | ||
""" | ||
""" | ||
interface EdgeGraphData { | ||
from: string; | ||
to: string; | ||
label?: string; | ||
id?: string; | ||
color?: string; | ||
dashes?: boolean; | ||
} | ||
""" | ||
# NOTE: may not be able to encapsulate edge in separate class due to from being a syntax token | ||
""" | ||
class Edge: | ||
|
||
def __init__(self,from: str, to: str,): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add space after comma |
||
self.fromnode | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use |
||
|
||
def __repr__(self): | ||
pass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why have an empty method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because it needs to be implemented. This was mainly meant to be a skeleton of a library, make it easier for people to contribute to the development of one set of dataTypes. Also we can't use the dataType's for one, json complains Node objects aren't serializable when calling two,
|
||
""" | ||
# NOTE: ran into issue Node object is not json serializable when ecapsulating in own class | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add a method in Node class to serialize the data into json. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks, I'm just now starting to actually learn about serialization due to this project and a personal rust project. could you point me in the direction of some documentation on how to do so? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @skewballfox you can simply create a dictionary of the desired form and then serialize it into JSON:
(this is an example for the This is a crude approach, which is inconvenient for classes with many fields, but in this case, I think it is quite applicable. Also such a way you can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @insolor this is similar to what I was doing in also I think I figured out why either I could come up with a generic method that does this in I think I might have an idea using |
||
""" | ||
class Node(DataType): | ||
def __init__(self, id: Union[int, str], label: Optional[str] = None): | ||
super().__init__() | ||
self.id = id | ||
if label is None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest simplifying to: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that is actually a hella useful feature I didn't know about. Thanks for that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, given the implementation, and what we are passing the information to, this likely wouldn't work, as the attribute would still be listed in the json output(at least with the method currently used to produce json. I'm not sure if this would be the case, but I'm trying to avoid all of the nodes being labeled There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you give an example? I'm not able to understand. |
||
self.label = id | ||
else: | ||
self.label = label | ||
""" | ||
|
||
|
||
class Graph(DataType): | ||
"""An implementation of the Graph data type for the visualizer | ||
|
||
Args: | ||
DataType (Union[Dict[str, list], Dict[int, list]]): | ||
either expects a dictionary with a list as values or a 2d array | ||
some representation of a basic graph | ||
""" | ||
|
||
def __init__(self, graph_data: Union[Dict[str, list], Dict[int, list]]): | ||
super().__init__() | ||
self.kind["graph"] = True | ||
# TODO get working for both a dictionary and an nxn array | ||
self.nodes = [] | ||
self.edges = [] | ||
if isinstance(graph_data, dict): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not check the type here. We expect graph data to be a dictionary otherwise we should have exception not silence it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, no, the reason being is not counting classes for graphs(just too much trouble to worry about at this moment), you can have a graph represented as a dict or 2d array. I intended after getting the dictionary implementation working to check |
||
for node in graph_data: | ||
self.nodes.append({"id": str(node)}) | ||
# TODO change prints to log statements | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like a too minor change to left a TODO for it and not implement it right away There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's partially because my experience implementing logs across a library is none, I've only implemented them in a single file before, and didn't try to make it where the log statement was different across classes. |
||
# print("node: ", node) | ||
# print("edges: ", graph_data[node]) | ||
for edge in graph_data[node]: | ||
# print("edge: ", graph_data[node][edge_i]) | ||
self.edges.append({"from": node, "to": edge}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from pyDataExtraction.commonTypes.base import DataType | ||
|
||
""" | ||
export interface Grid { | ||
kind: { array: true }; | ||
columnLabels?: { label?: string }[]; | ||
rows: { | ||
label?: string; | ||
columns: { | ||
content?: string; | ||
tag?: string; | ||
color?: string; | ||
}[]; | ||
}[]; | ||
markers?: { | ||
id: string; | ||
|
||
row: number; | ||
column: number; | ||
rows?: number; | ||
columns?: number; | ||
|
||
label?: string; | ||
color?: string; | ||
}[]; | ||
} | ||
""" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from pyDataExtraction.commonTypes.base import DataType | ||
|
||
""" | ||
export interface Plotly { | ||
kind: { plotly: true }; | ||
data: Partial<Plotly.Data>[]; | ||
} | ||
// See plotly docs for Plotly.Data. | ||
""" | ||
|
||
|
||
class Plotly(DataType): | ||
def __init__(self, data): | ||
super().__init__() | ||
self.kind["plotly"] = True |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
from typing import Optional | ||
from pyDataExtraction.commonTypes.base import DataType | ||
|
||
""" | ||
interface Text { | ||
kind: { text: true }; | ||
text: string; | ||
mimeType?: string; | ||
fileName?: string; | ||
} | ||
""" | ||
|
||
|
||
class Text(DataType): | ||
def __init__( | ||
self, | ||
text_data: str, | ||
mimeType: Optional[str] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's keep it to the pep-8 python style guide:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the variable names are mainly because of the use case and the method currently for doing so. right now I'm directly converting the respective dataTypes to Though since we already need to do some manipulations on the json representation before returning in the case of the btw if we got rid of the reliance on |
||
fileName: Optional[str] = None, | ||
): | ||
super().__init__() | ||
|
||
self.kind["text"] = True | ||
self.text = text_data | ||
if mimeType is not None: | ||
self.mimeType = mimeType | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what are you trying to achieve here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, that was probably a missing |
||
if fileName is None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest you wanted the opposite:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it may be when dealing with edge cases far removed from this or a leftover from python 2, but from what I keep reading, using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, while editing this, I just remembered why I checked. because this library currently relies on they weren't part of the object unless they were explicitly added to the object. this may be another reason to either find a better method of creating json objects, or use super to get the inherited |
||
self.fileName = fileName | ||
|
||
|
||
""" | ||
interface Svg extends Text { | ||
kind: { text: true; svg: true }; | ||
} | ||
""" | ||
|
||
|
||
class Svg(Text): | ||
def __init__( | ||
self, | ||
text_data: str, | ||
mimeType: Optional[str] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please name params in snake case: |
||
fileName: Optional[str] = None, | ||
): | ||
self.kind["svg"] = True | ||
super().__init__(text_data, mimeType, fileName) | ||
|
||
|
||
""" | ||
interface DotGraph extends Text { | ||
kind: { text: true; dotGraph: true }; | ||
} | ||
""" | ||
|
||
|
||
class DotGraph(Text): | ||
def __init__( | ||
self, | ||
text_data: str, | ||
mimeType: Optional[str] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use snake case: |
||
fileName: Optional[str] = None, | ||
): | ||
self.kind["dotGraph"] = True | ||
super().__init__(text_data, mimeType, fileName) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from typing import Union, Dict, Optional | ||
from abc import ABC, abstractmethod | ||
|
||
from pyDataExtraction.commonTypes import base | ||
from pyDataExtraction.commonTypes import Graph | ||
from pyDataExtraction.commonTypes import Grid | ||
from pyDataExtraction.commonTypes import Plotly | ||
from pyDataExtraction.commonTypes import Text |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import json | ||
from abc import ABC | ||
|
||
# import logging | ||
# TODO fix the import structure | ||
# TODO figure out how to set up logging across a python library | ||
|
||
|
||
class DataType(ABC): | ||
"""Abstract class for all supported dataTypesdataTypes | ||
|
||
Args: | ||
object ([type]): [description] | ||
""" | ||
|
||
def __init__(self): | ||
self.kind = {} | ||
super().__init__() | ||
|
||
def __repr__(self): | ||
"""returns json object format when printed or using str() | ||
""" | ||
return json.dumps(self.__dict__) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { | ||
getExpressionForDataExtractorApi, | ||
DataResult, | ||
ApiHasNotBeenInitializedCode, | ||
getExpressionToInitializeDataExtractorApi, | ||
DataExtractionResult, | ||
} from "@hediet/debug-visualizer-data-extraction"; | ||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession"; | ||
import { | ||
EvaluationEngine, | ||
Evaluator, | ||
EvaluationArgs, | ||
} from "./EvaluationEngine"; | ||
import { FormattedMessage } from "../../webviewContract"; | ||
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload"; | ||
|
||
registerUpdateReconciler(module); | ||
|
||
@hotClass(module) | ||
export class PyEvaluationEngine implements EvaluationEngine { | ||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined { | ||
const supportedDebugAdapters = [ | ||
"python", | ||
|
||
]; | ||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) { | ||
return new PyEvaluator(session); | ||
} | ||
return undefined; | ||
} | ||
} | ||
|
||
class PyEvaluator implements Evaluator { | ||
public readonly languageId = "python"; | ||
|
||
constructor(private readonly session: EnhancedDebugSession) { } | ||
|
||
private getContext(): "copy" | "repl" { | ||
if (this.session.session.type.startsWith("pwa-")) { | ||
return "copy"; | ||
} | ||
return "repl"; | ||
} | ||
|
||
public async evaluate({ | ||
expression, | ||
preferredExtractorId, | ||
frameId, | ||
}: EvaluationArgs): Promise< | ||
| { kind: "data"; result: DataExtractionResult } | ||
| { kind: "error"; message: FormattedMessage } | ||
> { | ||
while (true) { | ||
try { | ||
const preferredExtractorExpr = preferredExtractorId | ||
? `"${preferredExtractorId}"` | ||
: "undefined"; | ||
|
||
const body = `${getExpressionForDataExtractorApi()}.getData( | ||
e => (${expression}), | ||
expr => eval(expr), | ||
${preferredExtractorExpr} | ||
)`; | ||
|
||
const wrappedExpr = ` | ||
(() => { | ||
try { | ||
return ${body}; | ||
} catch (e) { | ||
return JSON.stringify({ | ||
kind: "Error", | ||
message: e.message, | ||
stack: e.stack | ||
}); | ||
} | ||
})() | ||
`; | ||
|
||
const reply = await this.session.evaluate({ | ||
expression: wrappedExpr, | ||
frameId, | ||
context: this.getContext(), | ||
}); | ||
const resultStr = reply.result; | ||
const jsonData = | ||
this.getContext() === "copy" | ||
? resultStr | ||
: resultStr.substr(1, resultStr.length - 2); | ||
const result = JSON.parse(jsonData) as DataResult; | ||
|
||
if (result.kind === "NoExtractors") { | ||
throw new Error("No extractors"); | ||
} else if (result.kind === "Error") { | ||
throw new Error(result.message); | ||
} else if (result.kind === "Data") { | ||
return { | ||
kind: "data", | ||
result: result.extractionResult, | ||
}; | ||
} else { | ||
throw new Error("Invalid Data"); | ||
} | ||
} catch (error) { | ||
const msg = error.message as string | undefined; | ||
if (msg && msg.includes(ApiHasNotBeenInitializedCode)) { | ||
if (await this.initializeApi(frameId)) { | ||
continue; | ||
} | ||
} | ||
|
||
return { | ||
kind: "error", | ||
message: error.message, | ||
}; | ||
} | ||
} | ||
} | ||
|
||
private async initializeApi(frameId: number | undefined): Promise<boolean> { | ||
try { | ||
// prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten. | ||
const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`; | ||
|
||
await this.session.evaluate({ | ||
expression, | ||
frameId, | ||
context: this.getContext(), | ||
}); | ||
|
||
return true; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add newline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can run flake8(for linting) and black(code formatting) in local(console) to do all such changes automatically and consistency will be maintained for all the contributors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I currently use black for formatting, though I've never used flake8, nor have I used black from the commandline. I know there's a precommit plugin for git that would call both to on attempted commits to make sure that they are used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You just have to install black in local(or in the virtual environment of python) and run
black <file_name>
This will do the formatting for the file.And about flake8, It's just that we can have a consistent style guide for python over this project which follows pep-8 guidelines.
As some of the comments by @mvoitko is regarding this only.
Just a suggestion 😄