jupyterpack 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +29 -0
- package/README.md +79 -0
- package/lib/document/iframePanel.d.ts +7 -0
- package/lib/document/iframePanel.js +20 -0
- package/lib/document/jupyterpackDocWidget.d.ts +9 -0
- package/lib/document/jupyterpackDocWidget.js +16 -0
- package/lib/document/plugin.d.ts +2 -0
- package/lib/document/plugin.js +31 -0
- package/lib/document/widgetFactory.d.ts +23 -0
- package/lib/document/widgetFactory.js +60 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -0
- package/lib/pythonWidget/connectionManager.d.ts +18 -0
- package/lib/pythonWidget/connectionManager.js +27 -0
- package/lib/pythonWidget/kernelExecutor.d.ts +27 -0
- package/lib/pythonWidget/kernelExecutor.js +104 -0
- package/lib/pythonWidget/pythonWidget.d.ts +13 -0
- package/lib/pythonWidget/pythonWidget.js +22 -0
- package/lib/pythonWidget/pythonWidgetModel.d.ts +29 -0
- package/lib/pythonWidget/pythonWidgetModel.js +75 -0
- package/lib/sandpackWidget/sandpackFilesModel.d.ts +27 -0
- package/lib/sandpackWidget/sandpackFilesModel.js +114 -0
- package/lib/sandpackWidget/sandpackPanel.d.ts +16 -0
- package/lib/sandpackWidget/sandpackPanel.js +52 -0
- package/lib/swConnection/comm_manager.d.ts +6 -0
- package/lib/swConnection/comm_manager.js +46 -0
- package/lib/swConnection/connection_manager.d.ts +18 -0
- package/lib/swConnection/connection_manager.js +27 -0
- package/lib/swConnection/index.d.ts +3 -0
- package/lib/swConnection/index.js +68 -0
- package/lib/swConnection/sw.d.ts +1 -0
- package/lib/swConnection/sw.js +49 -0
- package/lib/token.d.ts +3 -0
- package/lib/token.js +2 -0
- package/lib/tools.d.ts +2 -0
- package/lib/tools.js +17 -0
- package/lib/type.d.ts +38 -0
- package/lib/type.js +9 -0
- package/package.json +199 -0
- package/src/document/iframePanel.ts +25 -0
- package/src/document/jupyterpackDocWidget.ts +19 -0
- package/src/document/plugin.ts +44 -0
- package/src/document/widgetFactory.ts +79 -0
- package/src/index.ts +4 -0
- package/src/pythonWidget/connectionManager.ts +43 -0
- package/src/pythonWidget/kernelExecutor.ts +140 -0
- package/src/pythonWidget/pythonWidget.ts +34 -0
- package/src/pythonWidget/pythonWidgetModel.ts +107 -0
- package/src/sandpackWidget/sandpackFilesModel.ts +141 -0
- package/src/sandpackWidget/sandpackPanel.ts +82 -0
- package/src/swConnection/comm_manager.ts +53 -0
- package/src/swConnection/connection_manager.ts +43 -0
- package/src/swConnection/index.ts +93 -0
- package/src/swConnection/sw.ts +57 -0
- package/src/token.ts +6 -0
- package/src/tools.ts +18 -0
- package/src/type.ts +44 -0
- package/style/base.css +51 -0
- package/style/index.css +1 -0
- package/style/index.js +1 -0
package/package.json
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jupyterpack",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A JupyterLab extension for serving web app.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jupyter",
|
|
7
|
+
"jupyterlab",
|
|
8
|
+
"jupyterlab-extension"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/trungleduc/jupyterpack",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/trungleduc/jupyterpack/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "BSD-3-Clause",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Trung Le",
|
|
17
|
+
"email": "leductrungxf@gmail.com"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
|
|
21
|
+
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
|
|
22
|
+
"src/**/*.{ts,tsx}"
|
|
23
|
+
],
|
|
24
|
+
"main": "lib/index.js",
|
|
25
|
+
"types": "lib/index.d.ts",
|
|
26
|
+
"style": "style/index.css",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/trungleduc/jupyterpack.git"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build:demo": "cd demo && rm -rf .jupyterlite.doit.db _output && jupyter lite build .",
|
|
33
|
+
"update:demo": "node script/update-demo.mjs",
|
|
34
|
+
"build:worker": "webpack --config sw.webpack.config.js --mode development",
|
|
35
|
+
"build:worker:prod": "webpack --config sw.webpack.config.js --mode production",
|
|
36
|
+
"build": "jlpm build:lib && jlpm build:labextension:dev && jlpm build:worker",
|
|
37
|
+
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension && jlpm build:worker:prod",
|
|
38
|
+
"build:labextension": "jupyter labextension build .",
|
|
39
|
+
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
40
|
+
"build:lib": "tsc --sourceMap",
|
|
41
|
+
"build:lib:prod": "tsc",
|
|
42
|
+
"clean": "jlpm clean:lib",
|
|
43
|
+
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
|
|
44
|
+
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
|
|
45
|
+
"clean:labextension": "rimraf jupyterpack/labextension jupyterpack/_version.py",
|
|
46
|
+
"clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
|
|
47
|
+
"eslint": "jlpm eslint:check --fix",
|
|
48
|
+
"eslint:check": "eslint . --cache --ext .ts,.tsx",
|
|
49
|
+
"install:extension": "jlpm build",
|
|
50
|
+
"lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
|
|
51
|
+
"lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
|
|
52
|
+
"prettier": "jlpm prettier:base --write --list-different",
|
|
53
|
+
"prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
|
|
54
|
+
"prettier:check": "jlpm prettier:base --check",
|
|
55
|
+
"stylelint": "jlpm stylelint:check --fix",
|
|
56
|
+
"stylelint:check": "stylelint --cache \"style/**/*.css\"",
|
|
57
|
+
"watch": "run-p watch:src watch:labextension",
|
|
58
|
+
"watch:src": "tsc -w --sourceMap",
|
|
59
|
+
"watch:labextension": "jupyter labextension watch ."
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@codesandbox/sandpack-client": "^2.19.8",
|
|
63
|
+
"@jupyterlab/application": "^4.0.0",
|
|
64
|
+
"comlink": "^4.4.2"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@jupyterlab/builder": "^4.0.0",
|
|
68
|
+
"@types/json-schema": "^7.0.11",
|
|
69
|
+
"@types/react": "^18.0.26",
|
|
70
|
+
"@types/react-addons-linked-state-mixin": "^0.14.22",
|
|
71
|
+
"@types/serviceworker": "^0.0.152",
|
|
72
|
+
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
73
|
+
"@typescript-eslint/parser": "^6.1.0",
|
|
74
|
+
"copy-webpack-plugin": "^13.0.1",
|
|
75
|
+
"css-loader": "^6.7.1",
|
|
76
|
+
"eslint": "^8.36.0",
|
|
77
|
+
"eslint-config-prettier": "^8.8.0",
|
|
78
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
79
|
+
"npm-run-all2": "^7.0.1",
|
|
80
|
+
"prettier": "^3.0.0",
|
|
81
|
+
"rimraf": "^5.0.1",
|
|
82
|
+
"source-map-loader": "^1.0.2",
|
|
83
|
+
"style-loader": "^3.3.1",
|
|
84
|
+
"stylelint": "^15.10.1",
|
|
85
|
+
"stylelint-config-recommended": "^13.0.0",
|
|
86
|
+
"stylelint-config-standard": "^34.0.0",
|
|
87
|
+
"stylelint-csstree-validator": "^3.0.0",
|
|
88
|
+
"stylelint-prettier": "^4.0.0",
|
|
89
|
+
"ts-loader": "^9.2.6",
|
|
90
|
+
"typescript": "~5.8.0",
|
|
91
|
+
"webpack": "^5.76.3",
|
|
92
|
+
"webpack-cli": "^6.0.1",
|
|
93
|
+
"yjs": "^13.5.0"
|
|
94
|
+
},
|
|
95
|
+
"sideEffects": [
|
|
96
|
+
"style/*.css",
|
|
97
|
+
"style/index.js"
|
|
98
|
+
],
|
|
99
|
+
"styleModule": "style/index.js",
|
|
100
|
+
"publishConfig": {
|
|
101
|
+
"access": "public"
|
|
102
|
+
},
|
|
103
|
+
"jupyterlab": {
|
|
104
|
+
"extension": true,
|
|
105
|
+
"outputDir": "jupyterpack/labextension"
|
|
106
|
+
},
|
|
107
|
+
"eslintIgnore": [
|
|
108
|
+
"node_modules",
|
|
109
|
+
"demo",
|
|
110
|
+
"dist",
|
|
111
|
+
"coverage",
|
|
112
|
+
"**/*.d.ts"
|
|
113
|
+
],
|
|
114
|
+
"eslintConfig": {
|
|
115
|
+
"extends": [
|
|
116
|
+
"eslint:recommended",
|
|
117
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
118
|
+
"plugin:@typescript-eslint/recommended",
|
|
119
|
+
"plugin:prettier/recommended"
|
|
120
|
+
],
|
|
121
|
+
"parser": "@typescript-eslint/parser",
|
|
122
|
+
"parserOptions": {
|
|
123
|
+
"project": "tsconfig.json",
|
|
124
|
+
"sourceType": "module"
|
|
125
|
+
},
|
|
126
|
+
"plugins": [
|
|
127
|
+
"@typescript-eslint"
|
|
128
|
+
],
|
|
129
|
+
"rules": {
|
|
130
|
+
"@typescript-eslint/naming-convention": [
|
|
131
|
+
"error",
|
|
132
|
+
{
|
|
133
|
+
"selector": "interface",
|
|
134
|
+
"format": [
|
|
135
|
+
"PascalCase"
|
|
136
|
+
],
|
|
137
|
+
"custom": {
|
|
138
|
+
"regex": "^I[A-Z]",
|
|
139
|
+
"match": true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"@typescript-eslint/no-unused-vars": [
|
|
144
|
+
"warn",
|
|
145
|
+
{
|
|
146
|
+
"args": "none"
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
150
|
+
"@typescript-eslint/no-namespace": "off",
|
|
151
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
152
|
+
"@typescript-eslint/quotes": [
|
|
153
|
+
"error",
|
|
154
|
+
"single",
|
|
155
|
+
{
|
|
156
|
+
"avoidEscape": true,
|
|
157
|
+
"allowTemplateLiterals": false
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"curly": [
|
|
161
|
+
"error",
|
|
162
|
+
"all"
|
|
163
|
+
],
|
|
164
|
+
"eqeqeq": "error",
|
|
165
|
+
"prefer-arrow-callback": "error"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"prettier": {
|
|
169
|
+
"singleQuote": true,
|
|
170
|
+
"trailingComma": "none",
|
|
171
|
+
"arrowParens": "avoid",
|
|
172
|
+
"endOfLine": "auto",
|
|
173
|
+
"overrides": [
|
|
174
|
+
{
|
|
175
|
+
"files": "package.json",
|
|
176
|
+
"options": {
|
|
177
|
+
"tabWidth": 4
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
"stylelint": {
|
|
183
|
+
"extends": [
|
|
184
|
+
"stylelint-config-recommended",
|
|
185
|
+
"stylelint-config-standard",
|
|
186
|
+
"stylelint-prettier/recommended"
|
|
187
|
+
],
|
|
188
|
+
"plugins": [
|
|
189
|
+
"stylelint-csstree-validator"
|
|
190
|
+
],
|
|
191
|
+
"rules": {
|
|
192
|
+
"csstree/validator": true,
|
|
193
|
+
"property-no-vendor-prefix": null,
|
|
194
|
+
"selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
|
|
195
|
+
"selector-no-vendor-prefix": null,
|
|
196
|
+
"value-no-vendor-prefix": null
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Widget } from '@lumino/widgets';
|
|
2
|
+
|
|
3
|
+
export class IFramePanel extends Widget {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.addClass('jupyterpack-iframe-panel');
|
|
7
|
+
|
|
8
|
+
this._iframe = document.createElement('iframe');
|
|
9
|
+
this._spinner = document.createElement('div');
|
|
10
|
+
this._spinner.classList.add('jupyterpack-spinner');
|
|
11
|
+
this.node.appendChild(this._spinner);
|
|
12
|
+
this.node.appendChild(this._iframe);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
toggleSpinner(show: boolean): void {
|
|
16
|
+
if (show) {
|
|
17
|
+
this._spinner.style.display = 'unset';
|
|
18
|
+
} else {
|
|
19
|
+
this._spinner.style.display = 'none';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
protected _iframe: HTMLIFrameElement;
|
|
24
|
+
protected _spinner: HTMLDivElement;
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
2
|
+
|
|
3
|
+
export class JupyterPackDocWidget extends DocumentWidget {
|
|
4
|
+
constructor(options: DocumentWidget.IOptions) {
|
|
5
|
+
super(options);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Dispose of the resources held by the widget.
|
|
10
|
+
*/
|
|
11
|
+
dispose(): void {
|
|
12
|
+
this.content.dispose();
|
|
13
|
+
super.dispose();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
onResize = (msg: any): void => {
|
|
17
|
+
window.dispatchEvent(new Event('resize'));
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JupyterFrontEnd,
|
|
3
|
+
JupyterFrontEndPlugin
|
|
4
|
+
} from '@jupyterlab/application';
|
|
5
|
+
|
|
6
|
+
import { JupyterPackWidgetFactory } from './widgetFactory';
|
|
7
|
+
import { IConnectionManager } from '../type';
|
|
8
|
+
import { IConnectionManagerToken } from '../token';
|
|
9
|
+
|
|
10
|
+
const FACTORY = 'jupyterpack';
|
|
11
|
+
const CONTENT_TYPE = 'jupyterpack';
|
|
12
|
+
|
|
13
|
+
export const spkPlugin: JupyterFrontEndPlugin<void> = {
|
|
14
|
+
id: 'jupyterpack:spkplugin',
|
|
15
|
+
requires: [IConnectionManagerToken],
|
|
16
|
+
autoStart: true,
|
|
17
|
+
activate: (
|
|
18
|
+
app: JupyterFrontEnd,
|
|
19
|
+
connectionManager: IConnectionManager
|
|
20
|
+
): void => {
|
|
21
|
+
const widgetFactory = new JupyterPackWidgetFactory({
|
|
22
|
+
name: FACTORY,
|
|
23
|
+
modelName: 'text',
|
|
24
|
+
fileTypes: [CONTENT_TYPE],
|
|
25
|
+
defaultFor: [CONTENT_TYPE],
|
|
26
|
+
commands: app.commands,
|
|
27
|
+
manager: app.serviceManager,
|
|
28
|
+
connectionManager
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Registering the widget factory
|
|
32
|
+
app.docRegistry.addWidgetFactory(widgetFactory);
|
|
33
|
+
|
|
34
|
+
// register the filetype
|
|
35
|
+
app.docRegistry.addFileType({
|
|
36
|
+
name: CONTENT_TYPE,
|
|
37
|
+
displayName: 'SPK',
|
|
38
|
+
mimeTypes: ['text/json'],
|
|
39
|
+
extensions: ['.spk', '.SPK'],
|
|
40
|
+
fileFormat: 'json',
|
|
41
|
+
contentType: CONTENT_TYPE
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
|
|
2
|
+
import { Contents, ServiceManager } from '@jupyterlab/services';
|
|
3
|
+
import { CommandRegistry } from '@lumino/commands';
|
|
4
|
+
import { Panel } from '@lumino/widgets';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
IConnectionManager,
|
|
8
|
+
IJupyterPackFileFormat,
|
|
9
|
+
JupyterPackFramework
|
|
10
|
+
} from '../type';
|
|
11
|
+
import { SandpackPanel } from '../sandpackWidget/sandpackPanel';
|
|
12
|
+
import { JupyterPackDocWidget } from './jupyterpackDocWidget';
|
|
13
|
+
import { PythonWidgetModel } from '../pythonWidget/pythonWidgetModel';
|
|
14
|
+
import { UUID } from '@lumino/coreutils';
|
|
15
|
+
import { PythonWidget } from '../pythonWidget/pythonWidget';
|
|
16
|
+
|
|
17
|
+
interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
|
|
18
|
+
commands: CommandRegistry;
|
|
19
|
+
manager: ServiceManager.IManager;
|
|
20
|
+
connectionManager: IConnectionManager;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWidget> {
|
|
24
|
+
constructor(private options: IOptions) {
|
|
25
|
+
super(options);
|
|
26
|
+
this._contentsManager = options.manager.contents;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a new widget given a context.
|
|
31
|
+
*
|
|
32
|
+
* @param context Contains the information of the file
|
|
33
|
+
* @returns The widget
|
|
34
|
+
*/
|
|
35
|
+
protected createNewWidget(
|
|
36
|
+
context: DocumentRegistry.IContext<DocumentRegistry.IModel>
|
|
37
|
+
): JupyterPackDocWidget {
|
|
38
|
+
const content = new Panel();
|
|
39
|
+
content.addClass('jp-jupyterpack-document-panel');
|
|
40
|
+
context.ready.then(() => {
|
|
41
|
+
const jpackModel =
|
|
42
|
+
context.model.toJSON() as any as IJupyterPackFileFormat;
|
|
43
|
+
switch (jpackModel.framework) {
|
|
44
|
+
case JupyterPackFramework.REACT: {
|
|
45
|
+
const jpContent = new SandpackPanel({
|
|
46
|
+
context,
|
|
47
|
+
contentsManager: this._contentsManager
|
|
48
|
+
});
|
|
49
|
+
content.addWidget(jpContent);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case JupyterPackFramework.DASH: {
|
|
53
|
+
const model = new PythonWidgetModel({
|
|
54
|
+
context,
|
|
55
|
+
manager: this.options.manager,
|
|
56
|
+
contentsManager: this._contentsManager,
|
|
57
|
+
connectionManager: this.options.connectionManager
|
|
58
|
+
});
|
|
59
|
+
const pythonWidget = new PythonWidget({
|
|
60
|
+
model,
|
|
61
|
+
id: UUID.uuid4()
|
|
62
|
+
});
|
|
63
|
+
content.addWidget(pythonWidget);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
default: {
|
|
67
|
+
console.error('Unsupported framework');
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return new JupyterPackDocWidget({
|
|
73
|
+
context,
|
|
74
|
+
content
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private _contentsManager: Contents.IManager;
|
|
79
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { IConnectionManager, IDict, IKernelExecutor } from '../type';
|
|
2
|
+
import { UUID } from '@lumino/coreutils';
|
|
3
|
+
|
|
4
|
+
export class ConnectionManager implements IConnectionManager {
|
|
5
|
+
constructor(public instanceId: string) {}
|
|
6
|
+
|
|
7
|
+
async registerConnection(
|
|
8
|
+
kernelExecutor: IKernelExecutor
|
|
9
|
+
): Promise<{ instanceId: string; kernelClientId: string }> {
|
|
10
|
+
const uuid = UUID.uuid4();
|
|
11
|
+
|
|
12
|
+
this._kernelExecutors.set(uuid, kernelExecutor);
|
|
13
|
+
|
|
14
|
+
return { instanceId: this.instanceId, kernelClientId: uuid };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async generateResponse(options: {
|
|
18
|
+
kernelClientId: string;
|
|
19
|
+
urlPath: string;
|
|
20
|
+
method: string;
|
|
21
|
+
headers: IDict;
|
|
22
|
+
requestBody?: ArrayBuffer;
|
|
23
|
+
params?: string;
|
|
24
|
+
}): Promise<IDict | null> {
|
|
25
|
+
const { urlPath, kernelClientId, method, params, requestBody, headers } =
|
|
26
|
+
options;
|
|
27
|
+
const executor = this._kernelExecutors.get(kernelClientId);
|
|
28
|
+
if (!executor) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const response = await executor.getResponse({
|
|
33
|
+
urlPath,
|
|
34
|
+
method,
|
|
35
|
+
params,
|
|
36
|
+
headers,
|
|
37
|
+
requestBody
|
|
38
|
+
});
|
|
39
|
+
return response;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private _kernelExecutors = new Map<string, IKernelExecutor>();
|
|
43
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { KernelMessage, Session } from '@jupyterlab/services';
|
|
3
|
+
|
|
4
|
+
import { arrayBufferToBase64 } from '../tools';
|
|
5
|
+
import { IDict, IKernelExecutor } from '../type';
|
|
6
|
+
|
|
7
|
+
export class KernelExecutor implements IKernelExecutor {
|
|
8
|
+
constructor(options: KernelExecutor.IOptions) {
|
|
9
|
+
this._sessionConnection = options.sessionConnection;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get isDisposed(): boolean {
|
|
13
|
+
return this._isDisposed;
|
|
14
|
+
}
|
|
15
|
+
async init(options: {
|
|
16
|
+
initCode?: string;
|
|
17
|
+
instanceId: string;
|
|
18
|
+
kernelClientId: string;
|
|
19
|
+
}) {
|
|
20
|
+
const { initCode, instanceId, kernelClientId } = options;
|
|
21
|
+
const labBaseUrl = PageConfig.getOption('baseUrl');
|
|
22
|
+
const baseURL = URLExt.join(
|
|
23
|
+
labBaseUrl,
|
|
24
|
+
'extensions/jupyterpack/static',
|
|
25
|
+
instanceId,
|
|
26
|
+
'dash',
|
|
27
|
+
kernelClientId,
|
|
28
|
+
'/'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const osCode = `
|
|
32
|
+
import os
|
|
33
|
+
os.environ['DASH_URL_BASE_PATHNAME'] = '${baseURL}'
|
|
34
|
+
`;
|
|
35
|
+
await this.executeCode({ code: osCode });
|
|
36
|
+
if (initCode) {
|
|
37
|
+
await this.executeCode({ code: initCode });
|
|
38
|
+
}
|
|
39
|
+
const serverCode = `
|
|
40
|
+
import httpx, json, base64
|
|
41
|
+
__monstra_transport = httpx.WSGITransport(app=app.server)
|
|
42
|
+
def __monstra_get_response(method, url, headers, content=None, params=None):
|
|
43
|
+
decoded_content = None
|
|
44
|
+
if content is not None:
|
|
45
|
+
content = base64.b64decode(content)
|
|
46
|
+
decoded_content = content.decode()
|
|
47
|
+
with httpx.Client(transport=__monstra_transport, base_url="http://testserver") as client:
|
|
48
|
+
r = client.request(method, url, headers=headers, content=content, params=params)
|
|
49
|
+
response = {
|
|
50
|
+
"headers": dict(r.headers),
|
|
51
|
+
"content": r.text,
|
|
52
|
+
"status_code": r.status_code,
|
|
53
|
+
"original_request": {"method": method, "url": url, "content": decoded_content, "params": params, "headers": headers},
|
|
54
|
+
}
|
|
55
|
+
json_str = json.dumps(response)
|
|
56
|
+
b64_str = base64.b64encode(json_str.encode("utf-8")).decode("utf-8")
|
|
57
|
+
return b64_str
|
|
58
|
+
`;
|
|
59
|
+
await this.executeCode({ code: serverCode });
|
|
60
|
+
}
|
|
61
|
+
async getResponse(options: {
|
|
62
|
+
method: string;
|
|
63
|
+
urlPath: string;
|
|
64
|
+
headers: IDict;
|
|
65
|
+
requestBody?: ArrayBuffer;
|
|
66
|
+
params?: string;
|
|
67
|
+
}): Promise<IDict> {
|
|
68
|
+
const { method, urlPath, requestBody, params, headers } = options;
|
|
69
|
+
const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
|
|
70
|
+
const code = `__monstra_get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${content ? `"${content}"` : 'None'}, params=${params ? `"${params}"` : 'None'})`;
|
|
71
|
+
const raw = await this.executeCode({ code });
|
|
72
|
+
const jsonStr = atob(raw.slice(1, -1));
|
|
73
|
+
const obj = JSON.parse(jsonStr);
|
|
74
|
+
|
|
75
|
+
return obj;
|
|
76
|
+
}
|
|
77
|
+
async executeCode(
|
|
78
|
+
code: KernelMessage.IExecuteRequestMsg['content']
|
|
79
|
+
): Promise<string> {
|
|
80
|
+
const kernel = this._sessionConnection?.kernel;
|
|
81
|
+
if (!kernel) {
|
|
82
|
+
throw new Error('Session has no kernel.');
|
|
83
|
+
}
|
|
84
|
+
return new Promise<string>((resolve, reject) => {
|
|
85
|
+
const future = kernel.requestExecute(code, false, undefined);
|
|
86
|
+
const parentMsgid = future.msg.header.msg_id;
|
|
87
|
+
let executeResult = '';
|
|
88
|
+
future.onIOPub = (msg: KernelMessage.IIOPubMessage): void => {
|
|
89
|
+
const msgType = msg.header.msg_type;
|
|
90
|
+
switch (msgType) {
|
|
91
|
+
case 'execute_result': {
|
|
92
|
+
const content = (msg as KernelMessage.IExecuteResultMsg).content
|
|
93
|
+
.data['text/plain'] as string;
|
|
94
|
+
executeResult += content;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'status': {
|
|
98
|
+
if (
|
|
99
|
+
(msg as KernelMessage.IStatusMsg).content.execution_state ===
|
|
100
|
+
'idle' &&
|
|
101
|
+
msg.parent_header.msg_id === parentMsgid
|
|
102
|
+
) {
|
|
103
|
+
resolve(executeResult);
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case 'stream': {
|
|
108
|
+
const content = (msg as KernelMessage.IStreamMsg).content.text;
|
|
109
|
+
executeResult += content;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'error': {
|
|
113
|
+
console.error('Kernel operation failed', msg.content);
|
|
114
|
+
reject(msg.content);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
dispose(): void {
|
|
125
|
+
if (this._isDisposed) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this._isDisposed = true;
|
|
129
|
+
this._sessionConnection.dispose();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private _isDisposed: boolean = false;
|
|
133
|
+
private _sessionConnection: Session.ISessionConnection;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export namespace KernelExecutor {
|
|
137
|
+
export interface IOptions {
|
|
138
|
+
sessionConnection: Session.ISessionConnection;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PythonWidgetModel } from './pythonWidgetModel';
|
|
2
|
+
import { PageConfig } from '@jupyterlab/coreutils';
|
|
3
|
+
import { IFramePanel } from '../document/iframePanel';
|
|
4
|
+
|
|
5
|
+
export class PythonWidget extends IFramePanel {
|
|
6
|
+
constructor(options: PythonWidget.IOptions) {
|
|
7
|
+
super();
|
|
8
|
+
this._model = options.model;
|
|
9
|
+
this._model.initialize().then(connectionData => {
|
|
10
|
+
if (!connectionData) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const iframe = this._iframe;
|
|
14
|
+
const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
|
|
15
|
+
iframe.src = `${fullLabextensionsUrl}/jupyterpack/static/${connectionData.instanceId}/dash/${connectionData.kernelClientId}/`;
|
|
16
|
+
iframe.addEventListener('load', () => {
|
|
17
|
+
this.toggleSpinner(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get model(): PythonWidgetModel {
|
|
23
|
+
return this._model;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private _model: PythonWidgetModel;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export namespace PythonWidget {
|
|
30
|
+
export interface IOptions {
|
|
31
|
+
id: string;
|
|
32
|
+
model: PythonWidgetModel;
|
|
33
|
+
}
|
|
34
|
+
}
|