jupyterpack 0.2.1 → 0.4.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/README.md +2 -0
- package/lib/document/commands.d.ts +8 -0
- package/lib/document/commands.js +76 -0
- package/lib/document/iframePanel.d.ts +4 -1
- package/lib/document/plugin.d.ts +2 -1
- package/lib/document/plugin.js +22 -4
- package/lib/document/toolbar.d.ts +9 -0
- package/lib/document/toolbar.js +20 -0
- package/lib/document/widgetFactory.d.ts +2 -1
- package/lib/document/widgetFactory.js +15 -6
- package/lib/index.d.ts +1 -1
- package/lib/pythonServer/dash/dashServer.d.ts +22 -0
- package/lib/pythonServer/dash/dashServer.js +47 -0
- package/lib/pythonServer/index.d.ts +5 -0
- package/lib/pythonServer/index.js +9 -0
- package/lib/pythonServer/kernelExecutor.d.ts +71 -0
- package/lib/pythonServer/kernelExecutor.js +140 -0
- package/lib/pythonServer/streamlit/streamlitServer.d.ts +14 -0
- package/lib/pythonServer/streamlit/streamlitServer.js +51 -0
- package/lib/pythonServer/tornado/tornadoServer.d.ts +34 -0
- package/lib/pythonServer/tornado/tornadoServer.js +68 -0
- package/lib/pythonWidget/comm.d.ts +11 -0
- package/lib/pythonWidget/comm.js +52 -0
- package/lib/pythonWidget/pythonWidget.d.ts +6 -0
- package/lib/pythonWidget/pythonWidget.js +28 -3
- package/lib/pythonWidget/pythonWidgetModel.d.ts +27 -6
- package/lib/pythonWidget/pythonWidgetModel.js +101 -15
- package/lib/sandpackWidget/sandpackFilesModel.d.ts +6 -2
- package/lib/sandpackWidget/sandpackFilesModel.js +13 -2
- package/lib/sandpackWidget/sandpackPanel.d.ts +10 -1
- package/lib/sandpackWidget/sandpackPanel.js +38 -3
- package/lib/swConnection/index.js +2 -2
- package/lib/{pythonWidget/connectionManager.d.ts → swConnection/mainConnectionManager.d.ts} +10 -0
- package/lib/swConnection/mainConnectionManager.js +93 -0
- package/lib/swConnection/sw.js +1 -1
- package/lib/swConnection/swCommManager.d.ts +11 -0
- package/lib/swConnection/{comm_manager.js → swCommManager.js} +5 -0
- package/lib/token.d.ts +2 -1
- package/lib/token.js +1 -0
- package/lib/tools.d.ts +9 -0
- package/lib/tools.js +75 -0
- package/lib/type.d.ts +64 -3
- package/lib/type.js +2 -0
- package/lib/websocket/websocket.d.ts +0 -0
- package/lib/websocket/websocket.js +152 -0
- package/package.json +8 -5
- package/src/document/commands.ts +91 -0
- package/src/document/iframePanel.ts +4 -1
- package/src/document/plugin.ts +28 -7
- package/src/document/toolbar.ts +39 -0
- package/src/document/widgetFactory.ts +17 -6
- package/src/global.d.ts +9 -0
- package/src/pythonServer/dash/dashServer.ts +66 -0
- package/src/pythonServer/index.ts +18 -0
- package/src/pythonServer/kernelExecutor.ts +243 -0
- package/src/pythonServer/streamlit/streamlitServer.ts +67 -0
- package/src/pythonServer/tornado/tornadoServer.ts +97 -0
- package/src/pythonWidget/comm.ts +65 -0
- package/src/pythonWidget/pythonWidget.ts +38 -3
- package/src/pythonWidget/pythonWidgetModel.ts +155 -34
- package/src/sandpackWidget/sandpackFilesModel.ts +17 -3
- package/src/sandpackWidget/sandpackPanel.ts +45 -4
- package/src/swConnection/index.ts +5 -2
- package/src/swConnection/mainConnectionManager.ts +121 -0
- package/src/swConnection/sw.ts +1 -1
- package/src/swConnection/{comm_manager.ts → swCommManager.ts} +6 -0
- package/src/token.ts +5 -1
- package/src/tools.ts +91 -0
- package/src/type.ts +76 -4
- package/src/websocket/websocket.ts +216 -0
- package/style/base.css +7 -0
- package/style/icons/autoreload.svg +16 -0
- package/style/icons/box.svg +12 -0
- package/style/icons/externallink.svg +10 -0
- package/lib/pythonWidget/connectionManager.js +0 -27
- package/lib/pythonWidget/kernelExecutor.d.ts +0 -27
- package/lib/pythonWidget/kernelExecutor.js +0 -104
- package/lib/swConnection/comm_manager.d.ts +0 -6
- package/lib/swConnection/connection_manager.d.ts +0 -18
- package/lib/swConnection/connection_manager.js +0 -27
- package/src/pythonWidget/connectionManager.ts +0 -43
- package/src/pythonWidget/kernelExecutor.ts +0 -140
- package/src/swConnection/connection_manager.ts +0 -43
package/src/document/plugin.ts
CHANGED
|
@@ -4,20 +4,29 @@ import {
|
|
|
4
4
|
} from '@jupyterlab/application';
|
|
5
5
|
|
|
6
6
|
import { JupyterPackWidgetFactory } from './widgetFactory';
|
|
7
|
-
import { IConnectionManager } from '../type';
|
|
8
|
-
import { IConnectionManagerToken } from '../token';
|
|
7
|
+
import { IConnectionManager, IJupyterpackDocTracker } from '../type';
|
|
8
|
+
import { IConnectionManagerToken, IJupyterpackDocTrackerToken } from '../token';
|
|
9
|
+
import { WidgetTracker } from '@jupyterlab/apputils';
|
|
10
|
+
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
11
|
+
import { logoIcon } from '../tools';
|
|
12
|
+
import { addCommands } from './commands';
|
|
9
13
|
|
|
10
14
|
const FACTORY = 'jupyterpack';
|
|
11
15
|
const CONTENT_TYPE = 'jupyterpack';
|
|
12
16
|
|
|
13
|
-
export const spkPlugin: JupyterFrontEndPlugin<
|
|
17
|
+
export const spkPlugin: JupyterFrontEndPlugin<IJupyterpackDocTracker> = {
|
|
14
18
|
id: 'jupyterpack:spkplugin',
|
|
15
19
|
requires: [IConnectionManagerToken],
|
|
16
20
|
autoStart: true,
|
|
21
|
+
provides: IJupyterpackDocTrackerToken,
|
|
17
22
|
activate: (
|
|
18
23
|
app: JupyterFrontEnd,
|
|
19
24
|
connectionManager: IConnectionManager
|
|
20
|
-
):
|
|
25
|
+
): IJupyterpackDocTracker => {
|
|
26
|
+
const tracker = new WidgetTracker<DocumentWidget>({
|
|
27
|
+
namespace: FACTORY
|
|
28
|
+
});
|
|
29
|
+
addCommands(app.commands, tracker);
|
|
21
30
|
const widgetFactory = new JupyterPackWidgetFactory({
|
|
22
31
|
name: FACTORY,
|
|
23
32
|
modelName: 'text',
|
|
@@ -25,7 +34,8 @@ export const spkPlugin: JupyterFrontEndPlugin<void> = {
|
|
|
25
34
|
defaultFor: [CONTENT_TYPE],
|
|
26
35
|
commands: app.commands,
|
|
27
36
|
manager: app.serviceManager,
|
|
28
|
-
connectionManager
|
|
37
|
+
connectionManager,
|
|
38
|
+
tracker
|
|
29
39
|
});
|
|
30
40
|
|
|
31
41
|
// Registering the widget factory
|
|
@@ -35,10 +45,21 @@ export const spkPlugin: JupyterFrontEndPlugin<void> = {
|
|
|
35
45
|
app.docRegistry.addFileType({
|
|
36
46
|
name: CONTENT_TYPE,
|
|
37
47
|
displayName: 'SPK',
|
|
38
|
-
mimeTypes: ['
|
|
48
|
+
mimeTypes: ['application/json'],
|
|
39
49
|
extensions: ['.spk', '.SPK'],
|
|
40
50
|
fileFormat: 'json',
|
|
41
|
-
contentType: CONTENT_TYPE
|
|
51
|
+
contentType: CONTENT_TYPE,
|
|
52
|
+
icon: logoIcon
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
widgetFactory.widgetCreated.connect((_, widget) => {
|
|
56
|
+
widget.title.icon = logoIcon;
|
|
57
|
+
widget.context.pathChanged.connect(() => {
|
|
58
|
+
tracker.save(widget);
|
|
59
|
+
});
|
|
60
|
+
tracker.add(widget);
|
|
42
61
|
});
|
|
62
|
+
|
|
63
|
+
return tracker;
|
|
43
64
|
}
|
|
44
65
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandToolbarButton,
|
|
3
|
+
ReactiveToolbar
|
|
4
|
+
} from '@jupyterlab/ui-components';
|
|
5
|
+
import { CommandRegistry } from '@lumino/commands';
|
|
6
|
+
|
|
7
|
+
import { IJupyterpackDocTracker } from '../type';
|
|
8
|
+
import { CommandIDs } from './commands';
|
|
9
|
+
|
|
10
|
+
export class ToolbarWidget extends ReactiveToolbar {
|
|
11
|
+
constructor(options: {
|
|
12
|
+
tracker: IJupyterpackDocTracker;
|
|
13
|
+
commands: CommandRegistry;
|
|
14
|
+
}) {
|
|
15
|
+
super();
|
|
16
|
+
this.addClass('jupyterpack-toolbar');
|
|
17
|
+
this.addItem(
|
|
18
|
+
'Reload',
|
|
19
|
+
new CommandToolbarButton({
|
|
20
|
+
commands: options.commands,
|
|
21
|
+
id: CommandIDs.RELOAD
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
this.addItem(
|
|
25
|
+
'Toggle Auto Reload',
|
|
26
|
+
new CommandToolbarButton({
|
|
27
|
+
id: CommandIDs.TOGGLE_AUTORELOAD,
|
|
28
|
+
commands: options.commands
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
this.addItem(
|
|
32
|
+
'Open Specta',
|
|
33
|
+
new CommandToolbarButton({
|
|
34
|
+
id: CommandIDs.OPEN_SPECTA,
|
|
35
|
+
commands: options.commands
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
|
|
2
2
|
import { Contents, ServiceManager } from '@jupyterlab/services';
|
|
3
3
|
import { CommandRegistry } from '@lumino/commands';
|
|
4
|
+
import { UUID } from '@lumino/coreutils';
|
|
4
5
|
import { Panel } from '@lumino/widgets';
|
|
5
6
|
|
|
7
|
+
import { PythonWidget } from '../pythonWidget/pythonWidget';
|
|
8
|
+
import { PythonWidgetModel } from '../pythonWidget/pythonWidgetModel';
|
|
9
|
+
import { SandpackPanel } from '../sandpackWidget/sandpackPanel';
|
|
6
10
|
import {
|
|
7
11
|
IConnectionManager,
|
|
12
|
+
IJupyterpackDocTracker,
|
|
8
13
|
IJupyterPackFileFormat,
|
|
9
14
|
JupyterPackFramework
|
|
10
15
|
} from '../type';
|
|
11
|
-
import { SandpackPanel } from '../sandpackWidget/sandpackPanel';
|
|
12
16
|
import { JupyterPackDocWidget } from './jupyterpackDocWidget';
|
|
13
|
-
import {
|
|
14
|
-
import { UUID } from '@lumino/coreutils';
|
|
15
|
-
import { PythonWidget } from '../pythonWidget/pythonWidget';
|
|
17
|
+
import { ToolbarWidget } from './toolbar';
|
|
16
18
|
|
|
17
19
|
interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
|
|
18
20
|
commands: CommandRegistry;
|
|
19
21
|
manager: ServiceManager.IManager;
|
|
20
22
|
connectionManager: IConnectionManager;
|
|
23
|
+
tracker: IJupyterpackDocTracker;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWidget> {
|
|
@@ -49,8 +52,11 @@ export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWid
|
|
|
49
52
|
content.addWidget(jpContent);
|
|
50
53
|
break;
|
|
51
54
|
}
|
|
52
|
-
case JupyterPackFramework.DASH:
|
|
55
|
+
case JupyterPackFramework.DASH:
|
|
56
|
+
case JupyterPackFramework.STREAMLIT:
|
|
57
|
+
case JupyterPackFramework.TORNADO: {
|
|
53
58
|
const model = new PythonWidgetModel({
|
|
59
|
+
jpackModel,
|
|
54
60
|
context,
|
|
55
61
|
manager: this.options.manager,
|
|
56
62
|
contentsManager: this._contentsManager,
|
|
@@ -69,9 +75,14 @@ export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWid
|
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
77
|
});
|
|
78
|
+
const toolbar = new ToolbarWidget({
|
|
79
|
+
tracker: this.options.tracker,
|
|
80
|
+
commands: this.options.commands
|
|
81
|
+
});
|
|
72
82
|
return new JupyterPackDocWidget({
|
|
73
83
|
context,
|
|
74
|
-
content
|
|
84
|
+
content,
|
|
85
|
+
toolbar
|
|
75
86
|
});
|
|
76
87
|
}
|
|
77
88
|
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { stringOrNone } from '../../tools';
|
|
2
|
+
import { IDict, JupyterPackFramework } from '../../type';
|
|
3
|
+
import { KernelExecutor } from '../kernelExecutor';
|
|
4
|
+
|
|
5
|
+
export class DashServer extends KernelExecutor {
|
|
6
|
+
async init(options: {
|
|
7
|
+
initCode?: string;
|
|
8
|
+
instanceId: string;
|
|
9
|
+
kernelClientId: string;
|
|
10
|
+
}) {
|
|
11
|
+
await super.init(options);
|
|
12
|
+
const { initCode, instanceId, kernelClientId } = options;
|
|
13
|
+
|
|
14
|
+
const baseURL = this.buildBaseURL({
|
|
15
|
+
instanceId,
|
|
16
|
+
kernelClientId,
|
|
17
|
+
framework: JupyterPackFramework.DASH
|
|
18
|
+
});
|
|
19
|
+
await this.executeCode({
|
|
20
|
+
code: `
|
|
21
|
+
import os
|
|
22
|
+
os.environ["DASH_URL_BASE_PATHNAME"] = "${baseURL}"
|
|
23
|
+
`
|
|
24
|
+
});
|
|
25
|
+
if (initCode) {
|
|
26
|
+
await this.executeCode({ code: initCode });
|
|
27
|
+
}
|
|
28
|
+
const loaderCode = `
|
|
29
|
+
from jupyterpack.dash import DashServer
|
|
30
|
+
${this._DASH_SERVER_VAR} = DashServer(app, "${baseURL}")
|
|
31
|
+
`;
|
|
32
|
+
await this.executeCode({ code: loaderCode });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getResponseFunctionFactory(options: {
|
|
36
|
+
urlPath: string;
|
|
37
|
+
method: string;
|
|
38
|
+
headers: IDict;
|
|
39
|
+
params?: string;
|
|
40
|
+
content?: string;
|
|
41
|
+
}) {
|
|
42
|
+
const { method, urlPath, headers, params, content } = options;
|
|
43
|
+
const code = `${this._DASH_SERVER_VAR}.get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
|
|
44
|
+
return code;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async disposePythonServer(): Promise<void> {
|
|
48
|
+
await this.executeCode({ code: `${this._DASH_SERVER_VAR}.dispose()` });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async reloadPythonServer(options: {
|
|
52
|
+
entryPath?: string;
|
|
53
|
+
initCode?: string;
|
|
54
|
+
}): Promise<void> {
|
|
55
|
+
const { initCode } = options;
|
|
56
|
+
if (initCode) {
|
|
57
|
+
await this.executeCode({ code: initCode });
|
|
58
|
+
}
|
|
59
|
+
await this.executeCode(
|
|
60
|
+
{ code: `${this._DASH_SERVER_VAR}.reload(app)` },
|
|
61
|
+
true
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private _DASH_SERVER_VAR = '__jupyterpack_dash_server';
|
|
66
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IKernelExecutor, JupyterPackFramework } from '../type';
|
|
2
|
+
import { DashServer } from './dash/dashServer';
|
|
3
|
+
import { KernelExecutor } from './kernelExecutor';
|
|
4
|
+
import { StreamlitServer } from './streamlit/streamlitServer';
|
|
5
|
+
import { TornadoServer } from './tornado/tornadoServer';
|
|
6
|
+
|
|
7
|
+
type KernelExecutorConstructor = new (
|
|
8
|
+
options: KernelExecutor.IOptions
|
|
9
|
+
) => IKernelExecutor;
|
|
10
|
+
|
|
11
|
+
export const PYTHON_SERVER = new Map<
|
|
12
|
+
JupyterPackFramework,
|
|
13
|
+
KernelExecutorConstructor
|
|
14
|
+
>([
|
|
15
|
+
[JupyterPackFramework.DASH, DashServer],
|
|
16
|
+
[JupyterPackFramework.STREAMLIT, StreamlitServer],
|
|
17
|
+
[JupyterPackFramework.TORNADO, TornadoServer]
|
|
18
|
+
]);
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { KernelMessage, Session } from '@jupyterlab/services';
|
|
3
|
+
import stripAnsi from 'strip-ansi';
|
|
4
|
+
import {
|
|
5
|
+
arrayBufferToBase64,
|
|
6
|
+
base64ToArrayBuffer,
|
|
7
|
+
base64ToString,
|
|
8
|
+
isBinaryContentType
|
|
9
|
+
} from '../tools';
|
|
10
|
+
import { IDict, IKernelExecutor, JupyterPackFramework } from '../type';
|
|
11
|
+
import websocketPatch from '../websocket/websocket.js?raw';
|
|
12
|
+
|
|
13
|
+
export abstract class KernelExecutor implements IKernelExecutor {
|
|
14
|
+
constructor(options: KernelExecutor.IOptions) {
|
|
15
|
+
this._sessionConnection = options.sessionConnection;
|
|
16
|
+
this._wsPatch = websocketPatch.replaceAll('"use strict";', '');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get isDisposed(): boolean {
|
|
20
|
+
return this._isDisposed;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
abstract disposePythonServer(): Promise<void>;
|
|
24
|
+
abstract reloadPythonServer(options: {
|
|
25
|
+
entryPath?: string;
|
|
26
|
+
initCode?: string;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
|
|
29
|
+
abstract getResponseFunctionFactory(options: {
|
|
30
|
+
urlPath: string;
|
|
31
|
+
method: string;
|
|
32
|
+
headers: IDict;
|
|
33
|
+
params?: string;
|
|
34
|
+
content?: string;
|
|
35
|
+
}): string;
|
|
36
|
+
|
|
37
|
+
async init(options: {
|
|
38
|
+
entryPath?: string;
|
|
39
|
+
initCode?: string;
|
|
40
|
+
instanceId: string;
|
|
41
|
+
kernelClientId: string;
|
|
42
|
+
}): Promise<void> {
|
|
43
|
+
const patchCode = `
|
|
44
|
+
from jupyterpack.common import patch_all
|
|
45
|
+
patch_all()
|
|
46
|
+
`;
|
|
47
|
+
await this.executeCode({ code: patchCode });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
openWebsocketFunctionFactory(options: {
|
|
51
|
+
instanceId: string;
|
|
52
|
+
kernelId: string;
|
|
53
|
+
wsUrl: string;
|
|
54
|
+
protocol?: string;
|
|
55
|
+
}): string | undefined {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
sendWebsocketMessageFunctionFactory(options: {
|
|
60
|
+
instanceId: string;
|
|
61
|
+
kernelId: string;
|
|
62
|
+
wsUrl: string;
|
|
63
|
+
message: string;
|
|
64
|
+
}): string | undefined {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async openWebsocket(options: {
|
|
69
|
+
instanceId: string;
|
|
70
|
+
kernelId: string;
|
|
71
|
+
wsUrl: string;
|
|
72
|
+
protocol?: string;
|
|
73
|
+
}): Promise<void> {
|
|
74
|
+
const code = this.openWebsocketFunctionFactory(options);
|
|
75
|
+
if (code) {
|
|
76
|
+
await this.executeCode({ code });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async sendWebsocketMessage(options: {
|
|
81
|
+
instanceId: string;
|
|
82
|
+
kernelId: string;
|
|
83
|
+
wsUrl: string;
|
|
84
|
+
message: string;
|
|
85
|
+
}): Promise<void> {
|
|
86
|
+
const code = this.sendWebsocketMessageFunctionFactory(options);
|
|
87
|
+
if (code) {
|
|
88
|
+
await this.executeCode({ code });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getResponse(options: {
|
|
93
|
+
method: string;
|
|
94
|
+
urlPath: string;
|
|
95
|
+
headers: IDict;
|
|
96
|
+
requestBody?: ArrayBuffer;
|
|
97
|
+
params?: string;
|
|
98
|
+
}): Promise<IDict> {
|
|
99
|
+
const { method, urlPath, requestBody, params, headers } = options;
|
|
100
|
+
const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
|
|
101
|
+
const code = this.getResponseFunctionFactory({
|
|
102
|
+
method,
|
|
103
|
+
urlPath,
|
|
104
|
+
headers,
|
|
105
|
+
params,
|
|
106
|
+
content
|
|
107
|
+
});
|
|
108
|
+
const raw = await this.executeCode({ code }, true);
|
|
109
|
+
if (!raw) {
|
|
110
|
+
throw new Error(`Missing response for ${urlPath}`);
|
|
111
|
+
}
|
|
112
|
+
const jsonStr = raw.replaceAll("'", '');
|
|
113
|
+
const obj: {
|
|
114
|
+
headers: string;
|
|
115
|
+
status_code: number;
|
|
116
|
+
content: string;
|
|
117
|
+
} = JSON.parse(jsonStr);
|
|
118
|
+
const responseHeaders: IDict<string> = JSON.parse(atob(obj.headers));
|
|
119
|
+
const contentType: string | undefined =
|
|
120
|
+
responseHeaders?.['Content-Type'] ?? responseHeaders?.['content-type'];
|
|
121
|
+
let responseContent: string | Uint8Array;
|
|
122
|
+
|
|
123
|
+
if (isBinaryContentType(contentType)) {
|
|
124
|
+
responseContent = base64ToArrayBuffer(obj.content);
|
|
125
|
+
} else {
|
|
126
|
+
responseContent = base64ToString(obj.content);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (contentType && contentType.toLowerCase() === 'text/html') {
|
|
130
|
+
responseContent = (responseContent as string).replace(
|
|
131
|
+
'<head>',
|
|
132
|
+
`<head>\n<script>\n${this._wsPatch}\n</script>\n`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const decodedObj = {
|
|
137
|
+
status_code: obj.status_code,
|
|
138
|
+
headers: responseHeaders,
|
|
139
|
+
content: responseContent
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return decodedObj;
|
|
143
|
+
}
|
|
144
|
+
async executeCode(
|
|
145
|
+
code: KernelMessage.IExecuteRequestMsg['content'],
|
|
146
|
+
waitForResult?: boolean
|
|
147
|
+
): Promise<string | null> {
|
|
148
|
+
const kernel = this._sessionConnection?.kernel;
|
|
149
|
+
if (!kernel) {
|
|
150
|
+
throw new Error('Session has no kernel.');
|
|
151
|
+
}
|
|
152
|
+
return new Promise<string | null>((resolve, reject) => {
|
|
153
|
+
const future = kernel.requestExecute(code, false, undefined);
|
|
154
|
+
let executeResult = '';
|
|
155
|
+
future.onIOPub = (msg: KernelMessage.IIOPubMessage): void => {
|
|
156
|
+
const msgType = msg.header.msg_type;
|
|
157
|
+
|
|
158
|
+
switch (msgType) {
|
|
159
|
+
case 'execute_result': {
|
|
160
|
+
if (waitForResult) {
|
|
161
|
+
const content = (msg as KernelMessage.IExecuteResultMsg).content
|
|
162
|
+
.data['text/plain'] as string;
|
|
163
|
+
executeResult += content;
|
|
164
|
+
resolve(executeResult);
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case 'stream': {
|
|
169
|
+
const content = (msg as KernelMessage.IStreamMsg).content;
|
|
170
|
+
if (content.text.length === 0) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
if (content.name === 'stderr') {
|
|
174
|
+
console.error('Kernel stream:', content.text);
|
|
175
|
+
} else {
|
|
176
|
+
console.log('Kernel stream:', content.text);
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case 'error': {
|
|
181
|
+
console.error(
|
|
182
|
+
'Kernel operation failed',
|
|
183
|
+
code.code,
|
|
184
|
+
(msg.content as any).traceback
|
|
185
|
+
.map((it: string) => stripAnsi(it))
|
|
186
|
+
.join('\n')
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
reject(msg.content);
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
default:
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
if (!waitForResult) {
|
|
197
|
+
resolve(null);
|
|
198
|
+
// future.dispose() # TODO
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
dispose(): void {
|
|
204
|
+
if (this._isDisposed) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this._isDisposed = true;
|
|
208
|
+
this._sessionConnection.dispose();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
protected buildBaseURL(options: {
|
|
212
|
+
instanceId: string;
|
|
213
|
+
kernelClientId: string;
|
|
214
|
+
framework: JupyterPackFramework;
|
|
215
|
+
}) {
|
|
216
|
+
const { instanceId, kernelClientId, framework } = options;
|
|
217
|
+
const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
|
|
218
|
+
|
|
219
|
+
const baseURL = URLExt.join(
|
|
220
|
+
fullLabextensionsUrl,
|
|
221
|
+
'jupyterpack/static',
|
|
222
|
+
instanceId,
|
|
223
|
+
framework,
|
|
224
|
+
kernelClientId,
|
|
225
|
+
'/'
|
|
226
|
+
);
|
|
227
|
+
this._baseUrl = baseURL;
|
|
228
|
+
|
|
229
|
+
return baseURL;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
protected _baseUrl: string | undefined;
|
|
233
|
+
|
|
234
|
+
private _isDisposed: boolean = false;
|
|
235
|
+
private _sessionConnection: Session.ISessionConnection;
|
|
236
|
+
private _wsPatch: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export namespace KernelExecutor {
|
|
240
|
+
export interface IOptions {
|
|
241
|
+
sessionConnection: Session.ISessionConnection;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { TornadoServer } from '../tornado/tornadoServer';
|
|
3
|
+
|
|
4
|
+
export class StreamlitServer extends TornadoServer {
|
|
5
|
+
async init(options: {
|
|
6
|
+
entryPath?: string;
|
|
7
|
+
initCode?: string;
|
|
8
|
+
instanceId: string;
|
|
9
|
+
kernelClientId: string;
|
|
10
|
+
}) {
|
|
11
|
+
const { instanceId, kernelClientId, entryPath } = options;
|
|
12
|
+
if (!entryPath) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
'Missing streamlit entry path, please check your SPK file'
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const baseURL = this.buildBaseURL({
|
|
18
|
+
instanceId,
|
|
19
|
+
kernelClientId,
|
|
20
|
+
framework: JupyterPackFramework.STREAMLIT
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const patchCode = `
|
|
24
|
+
from jupyterpack.common import set_base_url_env, patch_tornado, patch_all
|
|
25
|
+
patch_all()
|
|
26
|
+
patch_tornado()
|
|
27
|
+
set_base_url_env("${baseURL}")
|
|
28
|
+
`;
|
|
29
|
+
await this.executeCode({ code: patchCode });
|
|
30
|
+
|
|
31
|
+
const bootstrapCode = `
|
|
32
|
+
from jupyterpack.streamlit import patch_streamlit
|
|
33
|
+
patch_streamlit()
|
|
34
|
+
`;
|
|
35
|
+
await this.executeCode({ code: bootstrapCode });
|
|
36
|
+
|
|
37
|
+
const stCode = `
|
|
38
|
+
from jupyterpack.streamlit import StreamlitServer, create_streamlit_app
|
|
39
|
+
__jupyterpack_st_server, __jupyterpack_tor_app = await create_streamlit_app("${entryPath}", "${baseURL}")
|
|
40
|
+
${this._SERVER_VAR} = StreamlitServer(__jupyterpack_tor_app, "${baseURL}", __jupyterpack_st_server)
|
|
41
|
+
`;
|
|
42
|
+
await this.executeCode({ code: stCode });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async reloadPythonServer(options: {
|
|
46
|
+
entryPath?: string;
|
|
47
|
+
initCode?: string;
|
|
48
|
+
}): Promise<void> {
|
|
49
|
+
const { entryPath } = options;
|
|
50
|
+
if (!entryPath || !this._baseUrl) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const reloadCode = `
|
|
54
|
+
${this._SERVER_VAR}.dispose()
|
|
55
|
+
__jupyterpack_st_server, __jupyterpack_tor_app = await create_streamlit_app("${entryPath}", "${this._baseUrl}")
|
|
56
|
+
${this._SERVER_VAR}.reload(__jupyterpack_tor_app, __jupyterpack_st_server)
|
|
57
|
+
`;
|
|
58
|
+
await this.executeCode(
|
|
59
|
+
{
|
|
60
|
+
code: reloadCode
|
|
61
|
+
},
|
|
62
|
+
true
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected _SERVER_VAR = '__jupyterpack_streamlit_server';
|
|
67
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { stringOrNone } from '../../tools';
|
|
2
|
+
import { IDict, JupyterPackFramework } from '../../type';
|
|
3
|
+
import { KernelExecutor } from '../kernelExecutor';
|
|
4
|
+
|
|
5
|
+
export class TornadoServer extends KernelExecutor {
|
|
6
|
+
async init(options: {
|
|
7
|
+
initCode?: string;
|
|
8
|
+
instanceId: string;
|
|
9
|
+
kernelClientId: string;
|
|
10
|
+
}) {
|
|
11
|
+
await super.init(options);
|
|
12
|
+
const { initCode, instanceId, kernelClientId } = options;
|
|
13
|
+
|
|
14
|
+
const baseURL = this.buildBaseURL({
|
|
15
|
+
instanceId,
|
|
16
|
+
kernelClientId,
|
|
17
|
+
framework: JupyterPackFramework.TORNADO
|
|
18
|
+
});
|
|
19
|
+
const bootstrapCode = `
|
|
20
|
+
from jupyterpack.common import set_base_url_env, patch_tornado
|
|
21
|
+
set_base_url_env("${baseURL}")
|
|
22
|
+
patch_tornado()
|
|
23
|
+
|
|
24
|
+
`;
|
|
25
|
+
await this.executeCode({ code: bootstrapCode });
|
|
26
|
+
if (initCode) {
|
|
27
|
+
const initCodeWithUrl = initCode.replaceAll('{{base_url}}', baseURL);
|
|
28
|
+
await this.executeCode({ code: initCodeWithUrl });
|
|
29
|
+
const loaderCode = `
|
|
30
|
+
from jupyterpack.tornado import TornadoServer
|
|
31
|
+
${this._SERVER_VAR} = TornadoServer(app, "${baseURL}")
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
await this.executeCode({ code: loaderCode });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getResponseFunctionFactory(options: {
|
|
39
|
+
urlPath: string;
|
|
40
|
+
method: string;
|
|
41
|
+
headers: IDict;
|
|
42
|
+
params?: string;
|
|
43
|
+
content?: string;
|
|
44
|
+
}) {
|
|
45
|
+
const { method, urlPath, headers, params, content } = options;
|
|
46
|
+
const code = `await ${this._SERVER_VAR}.get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
|
|
47
|
+
return code;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
openWebsocketFunctionFactory(options: {
|
|
51
|
+
instanceId: string;
|
|
52
|
+
kernelId: string;
|
|
53
|
+
wsUrl: string;
|
|
54
|
+
protocol?: string;
|
|
55
|
+
}): string {
|
|
56
|
+
const { instanceId, kernelId, wsUrl, protocol } = options;
|
|
57
|
+
|
|
58
|
+
const code = `await ${this._SERVER_VAR}.open_ws("${instanceId}", "${kernelId}", "${wsUrl}", ${stringOrNone(protocol)})`;
|
|
59
|
+
return code;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
sendWebsocketMessageFunctionFactory(options: {
|
|
63
|
+
instanceId: string;
|
|
64
|
+
kernelId: string;
|
|
65
|
+
wsUrl: string;
|
|
66
|
+
message: string;
|
|
67
|
+
}): string {
|
|
68
|
+
const { instanceId, kernelId, wsUrl, message } = options;
|
|
69
|
+
const code = `await ${this._SERVER_VAR}.receive_ws_message("${instanceId}", "${kernelId}", "${wsUrl}", '''${message}''')`;
|
|
70
|
+
return code;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async disposePythonServer(): Promise<void> {
|
|
74
|
+
await this.executeCode({
|
|
75
|
+
code: `${this._SERVER_VAR}.dispose()`
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async reloadPythonServer(options: {
|
|
80
|
+
entryPath?: string;
|
|
81
|
+
initCode?: string;
|
|
82
|
+
}): Promise<void> {
|
|
83
|
+
const { initCode } = options;
|
|
84
|
+
if (initCode) {
|
|
85
|
+
await this.executeCode({
|
|
86
|
+
code: initCode.replaceAll('{{base_url}}', this._baseUrl ?? '')
|
|
87
|
+
});
|
|
88
|
+
const reloadCode = `
|
|
89
|
+
${this._SERVER_VAR}.dispose()
|
|
90
|
+
${this._SERVER_VAR}.reload(app)
|
|
91
|
+
`;
|
|
92
|
+
await this.executeCode({ code: reloadCode }, true);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected _SERVER_VAR = '__jupyterpack_tornado_server';
|
|
97
|
+
}
|