jupyterpack 0.3.0 → 0.5.1
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 +1 -1
- 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 +14 -6
- package/lib/index.d.ts +1 -1
- package/lib/pythonServer/baseServer.d.ts +82 -0
- package/lib/pythonServer/baseServer.js +141 -0
- package/lib/pythonServer/dash/dashServer.d.ts +5 -17
- package/lib/pythonServer/dash/dashServer.js +19 -24
- package/lib/pythonServer/index.d.ts +3 -3
- package/lib/pythonServer/index.js +5 -1
- package/lib/pythonServer/kernelExecutor.d.ts +2 -53
- package/lib/pythonServer/kernelExecutor.js +5 -70
- package/lib/pythonServer/shiny/shinyServer.d.ts +14 -0
- package/lib/pythonServer/shiny/shinyServer.js +49 -0
- package/lib/pythonServer/starlette/starletteServer.d.ts +13 -0
- package/lib/pythonServer/starlette/starletteServer.js +49 -0
- package/lib/pythonServer/streamlit/streamlitServer.d.ts +6 -26
- package/lib/pythonServer/streamlit/streamlitServer.js +33 -41
- package/lib/pythonServer/tornado/tornadoServer.d.ts +6 -26
- package/lib/pythonServer/tornado/tornadoServer.js +28 -36
- package/lib/pythonWidget/comm.d.ts +11 -0
- package/lib/pythonWidget/comm.js +52 -0
- package/lib/pythonWidget/pythonWidget.d.ts +5 -0
- package/lib/pythonWidget/pythonWidget.js +19 -0
- package/lib/pythonWidget/pythonWidgetModel.d.ts +16 -4
- package/lib/pythonWidget/pythonWidgetModel.js +77 -13
- 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/mainConnectionManager.d.ts +4 -4
- package/lib/swConnection/mainConnectionManager.js +23 -12
- package/lib/token.d.ts +2 -1
- package/lib/token.js +1 -0
- package/lib/tools.d.ts +6 -0
- package/lib/tools.js +25 -0
- package/lib/type.d.ts +39 -4
- package/lib/type.js +2 -0
- package/lib/websocket/websocket.js +5 -1
- package/package.json +6 -6
- 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 +16 -6
- package/src/global.d.ts +5 -0
- package/src/pythonServer/baseServer.ts +245 -0
- package/src/pythonServer/dash/dashServer.ts +25 -35
- package/src/pythonServer/index.ts +9 -5
- package/src/pythonServer/kernelExecutor.ts +8 -147
- package/src/pythonServer/shiny/shinyServer.ts +62 -0
- package/src/pythonServer/starlette/starletteServer.ts +59 -0
- package/src/pythonServer/streamlit/streamlitServer.ts +40 -62
- package/src/pythonServer/tornado/tornadoServer.ts +33 -60
- package/src/pythonWidget/comm.ts +65 -0
- package/src/pythonWidget/pythonWidget.ts +19 -1
- package/src/pythonWidget/pythonWidgetModel.ts +107 -20
- package/src/sandpackWidget/sandpackFilesModel.ts +17 -3
- package/src/sandpackWidget/sandpackPanel.ts +45 -4
- package/src/swConnection/mainConnectionManager.ts +28 -20
- package/src/token.ts +5 -1
- package/src/tools.ts +31 -0
- package/src/type.ts +46 -7
- package/src/websocket/websocket.ts +9 -1
- 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/pythonServer/common/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/common/generatedPythonFiles.js +0 -72
- package/lib/pythonServer/dash/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/dash/generatedPythonFiles.js +0 -31
- package/lib/pythonServer/streamlit/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/streamlit/generatedPythonFiles.js +0 -147
- package/lib/pythonServer/tornado/generatedPythonFiles.d.ts +0 -3
- package/lib/pythonServer/tornado/generatedPythonFiles.js +0 -456
- package/src/pythonServer/common/generatedPythonFiles.ts +0 -73
- package/src/pythonServer/dash/generatedPythonFiles.ts +0 -32
- package/src/pythonServer/streamlit/generatedPythonFiles.ts +0 -148
- package/src/pythonServer/tornado/generatedPythonFiles.ts +0 -457
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { refreshIcon } from '@jupyterlab/ui-components';
|
|
2
|
+
import { CommandRegistry } from '@lumino/commands';
|
|
3
|
+
import { Panel } from '@lumino/widgets';
|
|
4
|
+
|
|
5
|
+
import { autoReloadIcon, linkIcon } from '../tools';
|
|
6
|
+
import { IJupyterpackDocTracker } from '../type';
|
|
7
|
+
import { IFramePanel } from './iframePanel';
|
|
8
|
+
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
9
|
+
|
|
10
|
+
export const CommandIDs = {
|
|
11
|
+
RELOAD: 'jupyterpack:reload',
|
|
12
|
+
TOGGLE_AUTORELOAD: 'jupyterpack:toggleAutoreload',
|
|
13
|
+
OPEN_SPECTA: 'jupyterpack:openInSpecta'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const labBaseUrl = PageConfig.getOption('baseUrl');
|
|
17
|
+
|
|
18
|
+
function getCurrentIframPanel(
|
|
19
|
+
tracker: IJupyterpackDocTracker
|
|
20
|
+
): IFramePanel | undefined {
|
|
21
|
+
const current = tracker.currentWidget?.content as Panel | undefined;
|
|
22
|
+
if (!current) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const widget = current.widgets[0] as IFramePanel | undefined;
|
|
26
|
+
if (!widget) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
return widget;
|
|
30
|
+
}
|
|
31
|
+
export function addCommands(
|
|
32
|
+
commands: CommandRegistry,
|
|
33
|
+
tracker: IJupyterpackDocTracker
|
|
34
|
+
) {
|
|
35
|
+
commands.addCommand(CommandIDs.RELOAD, {
|
|
36
|
+
caption: 'Reload',
|
|
37
|
+
isEnabled: () => {
|
|
38
|
+
return tracker.currentWidget !== null;
|
|
39
|
+
},
|
|
40
|
+
icon: refreshIcon,
|
|
41
|
+
execute: async () => {
|
|
42
|
+
const widget = getCurrentIframPanel(tracker);
|
|
43
|
+
if (widget) {
|
|
44
|
+
await widget.reload();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const commandState = { toggled: false };
|
|
49
|
+
commands.addCommand(CommandIDs.TOGGLE_AUTORELOAD, {
|
|
50
|
+
isEnabled: () => {
|
|
51
|
+
return tracker.currentWidget !== null;
|
|
52
|
+
},
|
|
53
|
+
isToggled: () => {
|
|
54
|
+
const widget = getCurrentIframPanel(tracker);
|
|
55
|
+
return Boolean(widget?.autoreload);
|
|
56
|
+
},
|
|
57
|
+
icon: autoReloadIcon,
|
|
58
|
+
caption: e => {
|
|
59
|
+
return commandState.toggled
|
|
60
|
+
? 'Auto-reload enabled'
|
|
61
|
+
: 'Auto-reload disabled';
|
|
62
|
+
},
|
|
63
|
+
execute: async () => {
|
|
64
|
+
const widget = getCurrentIframPanel(tracker);
|
|
65
|
+
if (widget) {
|
|
66
|
+
widget.autoreload = !widget?.autoreload;
|
|
67
|
+
|
|
68
|
+
commands.notifyCommandChanged(CommandIDs.TOGGLE_AUTORELOAD);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
commands.addCommand(CommandIDs.OPEN_SPECTA, {
|
|
73
|
+
caption: 'Open in Specta',
|
|
74
|
+
isEnabled: () => {
|
|
75
|
+
return tracker.currentWidget !== null;
|
|
76
|
+
},
|
|
77
|
+
icon: linkIcon,
|
|
78
|
+
execute: async () => {
|
|
79
|
+
const context = tracker.currentWidget?.context;
|
|
80
|
+
if (!context) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const spectaUrl = new URL(
|
|
84
|
+
URLExt.join(labBaseUrl, 'specta'),
|
|
85
|
+
window.location.origin
|
|
86
|
+
);
|
|
87
|
+
spectaUrl.searchParams.set('path', context.path);
|
|
88
|
+
window.open(spectaUrl.toString(), '_blank');
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Widget } from '@lumino/widgets';
|
|
2
2
|
|
|
3
|
-
export class IFramePanel extends Widget {
|
|
3
|
+
export abstract class IFramePanel extends Widget {
|
|
4
4
|
constructor() {
|
|
5
5
|
super();
|
|
6
6
|
this.addClass('jupyterpack-iframe-panel');
|
|
@@ -20,6 +20,9 @@ export class IFramePanel extends Widget {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
abstract reload(): Promise<void>;
|
|
24
|
+
abstract autoreload: boolean;
|
|
25
|
+
abstract isReady: Promise<void>;
|
|
23
26
|
protected _iframe: HTMLIFrameElement;
|
|
24
27
|
protected _spinner: HTMLDivElement;
|
|
25
28
|
}
|
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> {
|
|
@@ -51,7 +54,9 @@ export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWid
|
|
|
51
54
|
}
|
|
52
55
|
case JupyterPackFramework.DASH:
|
|
53
56
|
case JupyterPackFramework.STREAMLIT:
|
|
54
|
-
case JupyterPackFramework.TORNADO:
|
|
57
|
+
case JupyterPackFramework.TORNADO:
|
|
58
|
+
case JupyterPackFramework.STARLETTE:
|
|
59
|
+
case JupyterPackFramework.SHINY: {
|
|
55
60
|
const model = new PythonWidgetModel({
|
|
56
61
|
jpackModel,
|
|
57
62
|
context,
|
|
@@ -72,9 +77,14 @@ export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWid
|
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
});
|
|
80
|
+
const toolbar = new ToolbarWidget({
|
|
81
|
+
tracker: this.options.tracker,
|
|
82
|
+
commands: this.options.commands
|
|
83
|
+
});
|
|
75
84
|
return new JupyterPackDocWidget({
|
|
76
85
|
context,
|
|
77
|
-
content
|
|
86
|
+
content,
|
|
87
|
+
toolbar
|
|
78
88
|
});
|
|
79
89
|
}
|
|
80
90
|
|
package/src/global.d.ts
CHANGED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { KernelExecutor } from './kernelExecutor';
|
|
2
|
+
import websocketPatch from '../websocket/websocket.js?raw';
|
|
3
|
+
import {
|
|
4
|
+
IDict,
|
|
5
|
+
IBasePythonServer,
|
|
6
|
+
IKernelExecutor,
|
|
7
|
+
JupyterPackFramework
|
|
8
|
+
} from '../type';
|
|
9
|
+
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
10
|
+
import {
|
|
11
|
+
arrayBufferToBase64,
|
|
12
|
+
base64ToArrayBuffer,
|
|
13
|
+
base64ToString,
|
|
14
|
+
isBinaryContentType,
|
|
15
|
+
stringOrNone
|
|
16
|
+
} from '../tools';
|
|
17
|
+
|
|
18
|
+
export abstract class BasePythonServer implements IBasePythonServer {
|
|
19
|
+
constructor(options: KernelExecutor.IOptions) {
|
|
20
|
+
this._kernelExecutor = new KernelExecutor(options);
|
|
21
|
+
this._wsPatch = websocketPatch.replaceAll('"use strict";', '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
abstract reloadPythonServer(options: {
|
|
25
|
+
entryPath?: string;
|
|
26
|
+
initCode?: string;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
|
|
29
|
+
get isDisposed(): boolean {
|
|
30
|
+
return this._isDisposed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get kernelExecutor() {
|
|
34
|
+
return this._kernelExecutor;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
dispose(): void {
|
|
38
|
+
if (this._isDisposed) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this._isDisposed = true;
|
|
42
|
+
this.disposePythonServer()
|
|
43
|
+
.then(() => {
|
|
44
|
+
this._kernelExecutor.dispose();
|
|
45
|
+
})
|
|
46
|
+
.catch(console.error);
|
|
47
|
+
}
|
|
48
|
+
async init(options: {
|
|
49
|
+
entryPath?: string;
|
|
50
|
+
initCode?: string;
|
|
51
|
+
instanceId: string;
|
|
52
|
+
kernelClientId: string;
|
|
53
|
+
}): Promise<void> {
|
|
54
|
+
const patchCode = `
|
|
55
|
+
from jupyterpack.common import patch_all
|
|
56
|
+
patch_all()
|
|
57
|
+
`;
|
|
58
|
+
await this._kernelExecutor.executeCode({ code: patchCode });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async disposePythonServer(): Promise<void> {
|
|
62
|
+
await this.kernelExecutor.executeCode({
|
|
63
|
+
code: `${this._server_var}.dispose()`
|
|
64
|
+
});
|
|
65
|
+
for (const element of this._openedWebsockets) {
|
|
66
|
+
await this.closeWebsocket(element);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getResponseFunctionFactory(options: {
|
|
71
|
+
urlPath: string;
|
|
72
|
+
method: string;
|
|
73
|
+
headers: IDict;
|
|
74
|
+
params?: string;
|
|
75
|
+
content?: string;
|
|
76
|
+
}) {
|
|
77
|
+
const { method, urlPath, headers, params, content } = options;
|
|
78
|
+
const code = `await ${this._server_var}.get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
|
|
79
|
+
return code;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
openWebsocketFunctionFactory(options: {
|
|
83
|
+
instanceId: string;
|
|
84
|
+
kernelId: string;
|
|
85
|
+
wsUrl: string;
|
|
86
|
+
protocol?: string;
|
|
87
|
+
}): string {
|
|
88
|
+
const { instanceId, kernelId, wsUrl, protocol } = options;
|
|
89
|
+
const code = `await ${this._server_var}.open_ws("${instanceId}", "${kernelId}", "${wsUrl}", ${stringOrNone(protocol)})`;
|
|
90
|
+
return code;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
closeWebsocketFunctionFactory(options: {
|
|
94
|
+
instanceId: string;
|
|
95
|
+
kernelId: string;
|
|
96
|
+
wsUrl: string;
|
|
97
|
+
}): string {
|
|
98
|
+
const { instanceId, kernelId, wsUrl } = options;
|
|
99
|
+
const code = `await ${this._server_var}.close_ws("${instanceId}", "${kernelId}", "${wsUrl}")`;
|
|
100
|
+
return code;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
sendWebsocketMessageFunctionFactory(options: {
|
|
104
|
+
instanceId: string;
|
|
105
|
+
kernelId: string;
|
|
106
|
+
wsUrl: string;
|
|
107
|
+
message: string;
|
|
108
|
+
}): string {
|
|
109
|
+
const { instanceId, kernelId, wsUrl, message } = options;
|
|
110
|
+
const code = `await ${this._server_var}.receive_ws_message("${instanceId}", "${kernelId}", "${wsUrl}", '''${message}''')`;
|
|
111
|
+
return code;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async openWebsocket(options: {
|
|
115
|
+
instanceId: string;
|
|
116
|
+
kernelId: string;
|
|
117
|
+
wsUrl: string;
|
|
118
|
+
protocol?: string;
|
|
119
|
+
}): Promise<void> {
|
|
120
|
+
const code = this.openWebsocketFunctionFactory(options);
|
|
121
|
+
if (code) {
|
|
122
|
+
try {
|
|
123
|
+
await this._kernelExecutor.executeCode({ code });
|
|
124
|
+
this._openedWebsockets.push(options);
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error('Failed to open websocket', e);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
throw new Error('Missing websocket open code');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async closeWebsocket(options: {
|
|
134
|
+
instanceId: string;
|
|
135
|
+
kernelId: string;
|
|
136
|
+
wsUrl: string;
|
|
137
|
+
}): Promise<void> {
|
|
138
|
+
const code = this.closeWebsocketFunctionFactory(options);
|
|
139
|
+
if (code) {
|
|
140
|
+
await this._kernelExecutor.executeCode({ code });
|
|
141
|
+
} else {
|
|
142
|
+
throw new Error('Missing websocket close code');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async sendWebsocketMessage(options: {
|
|
147
|
+
instanceId: string;
|
|
148
|
+
kernelId: string;
|
|
149
|
+
wsUrl: string;
|
|
150
|
+
message: string;
|
|
151
|
+
}): Promise<void> {
|
|
152
|
+
const code = this.sendWebsocketMessageFunctionFactory(options);
|
|
153
|
+
if (code) {
|
|
154
|
+
await this._kernelExecutor.executeCode({ code });
|
|
155
|
+
} else {
|
|
156
|
+
throw new Error('Missing websocket send code');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async getResponse(options: {
|
|
161
|
+
method: string;
|
|
162
|
+
urlPath: string;
|
|
163
|
+
headers: IDict;
|
|
164
|
+
requestBody?: ArrayBuffer;
|
|
165
|
+
params?: string;
|
|
166
|
+
}): Promise<IDict> {
|
|
167
|
+
const { method, urlPath, requestBody, params, headers } = options;
|
|
168
|
+
const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
|
|
169
|
+
const code = this.getResponseFunctionFactory({
|
|
170
|
+
method,
|
|
171
|
+
urlPath,
|
|
172
|
+
headers,
|
|
173
|
+
params,
|
|
174
|
+
content
|
|
175
|
+
});
|
|
176
|
+
const raw = await this._kernelExecutor.executeCode({ code }, true);
|
|
177
|
+
if (!raw) {
|
|
178
|
+
throw new Error(`Missing response for ${urlPath}`);
|
|
179
|
+
}
|
|
180
|
+
const jsonStr = raw.replaceAll("'", '');
|
|
181
|
+
const obj: {
|
|
182
|
+
headers: string;
|
|
183
|
+
status_code: number;
|
|
184
|
+
content: string;
|
|
185
|
+
} = JSON.parse(jsonStr);
|
|
186
|
+
const responseHeaders: IDict<string> = JSON.parse(atob(obj.headers));
|
|
187
|
+
const contentType: string | undefined =
|
|
188
|
+
responseHeaders?.['Content-Type'] ?? responseHeaders?.['content-type'];
|
|
189
|
+
let responseContent: string | Uint8Array;
|
|
190
|
+
|
|
191
|
+
if (isBinaryContentType(contentType)) {
|
|
192
|
+
responseContent = base64ToArrayBuffer(obj.content);
|
|
193
|
+
} else {
|
|
194
|
+
responseContent = base64ToString(obj.content);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (contentType && contentType.toLowerCase().includes('text/html')) {
|
|
198
|
+
responseContent = (responseContent as string).replace(
|
|
199
|
+
'<head>',
|
|
200
|
+
`<head>\n<script>\n${this._wsPatch}\n</script>\n`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const decodedObj = {
|
|
205
|
+
status_code: obj.status_code,
|
|
206
|
+
headers: responseHeaders,
|
|
207
|
+
content: responseContent
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return decodedObj;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
protected buildBaseURL(options: {
|
|
214
|
+
instanceId: string;
|
|
215
|
+
kernelClientId: string;
|
|
216
|
+
framework: JupyterPackFramework;
|
|
217
|
+
}) {
|
|
218
|
+
const { instanceId, kernelClientId, framework } = options;
|
|
219
|
+
const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
|
|
220
|
+
|
|
221
|
+
const baseURL = URLExt.join(
|
|
222
|
+
fullLabextensionsUrl,
|
|
223
|
+
'jupyterpack/static',
|
|
224
|
+
instanceId,
|
|
225
|
+
framework,
|
|
226
|
+
kernelClientId,
|
|
227
|
+
'/'
|
|
228
|
+
);
|
|
229
|
+
this._baseUrl = baseURL;
|
|
230
|
+
|
|
231
|
+
return baseURL;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
protected _baseUrl: string | undefined;
|
|
235
|
+
protected readonly _openedWebsockets: {
|
|
236
|
+
instanceId: string;
|
|
237
|
+
kernelId: string;
|
|
238
|
+
wsUrl: string;
|
|
239
|
+
}[] = [];
|
|
240
|
+
protected readonly _server_var = '__jupyterpack_python_server';
|
|
241
|
+
|
|
242
|
+
private _kernelExecutor: IKernelExecutor;
|
|
243
|
+
private _isDisposed: boolean = false;
|
|
244
|
+
private _wsPatch: string;
|
|
245
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { patch } from '../common/generatedPythonFiles';
|
|
4
|
-
import { KernelExecutor } from '../kernelExecutor';
|
|
5
|
-
import { bootstrap, dashLoader } from './generatedPythonFiles';
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { BasePythonServer } from '../baseServer';
|
|
6
3
|
|
|
7
|
-
export class DashServer extends
|
|
4
|
+
export class DashServer extends BasePythonServer {
|
|
8
5
|
async init(options: {
|
|
9
6
|
initCode?: string;
|
|
10
7
|
instanceId: string;
|
|
@@ -18,40 +15,33 @@ export class DashServer extends KernelExecutor {
|
|
|
18
15
|
kernelClientId,
|
|
19
16
|
framework: JupyterPackFramework.DASH
|
|
20
17
|
});
|
|
21
|
-
await this.executeCode({
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
await this.kernelExecutor.executeCode({
|
|
19
|
+
code: `
|
|
20
|
+
import os
|
|
21
|
+
os.environ["DASH_URL_BASE_PATHNAME"] = "${baseURL}"
|
|
22
|
+
`
|
|
24
23
|
});
|
|
25
24
|
if (initCode) {
|
|
26
|
-
await this.executeCode({ code: initCode });
|
|
25
|
+
await this.kernelExecutor.executeCode({ code: initCode });
|
|
27
26
|
}
|
|
28
|
-
|
|
27
|
+
const loaderCode = `
|
|
28
|
+
from jupyterpack.dash import DashServer
|
|
29
|
+
${this._server_var} = DashServer(app, "${baseURL}")
|
|
30
|
+
`;
|
|
31
|
+
await this.kernelExecutor.executeCode({ code: loaderCode });
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
headers: IDict;
|
|
35
|
-
params?: string;
|
|
36
|
-
content?: string;
|
|
37
|
-
}) {
|
|
38
|
-
const { method, urlPath, headers, params, content } = options;
|
|
39
|
-
const code = `${this.DASH_GET_RESPONSE_FUNCTION}("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
|
|
40
|
-
return code;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async disposePythonServer(): Promise<void> {
|
|
44
|
-
//no-op
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async openWebsocket(options: {
|
|
48
|
-
instanceId: string;
|
|
49
|
-
kernelId: string;
|
|
50
|
-
wsUrl: string;
|
|
51
|
-
protocol?: string;
|
|
34
|
+
async reloadPythonServer(options: {
|
|
35
|
+
entryPath?: string;
|
|
36
|
+
initCode?: string;
|
|
52
37
|
}): Promise<void> {
|
|
53
|
-
|
|
38
|
+
const { initCode } = options;
|
|
39
|
+
if (initCode) {
|
|
40
|
+
await this.kernelExecutor.executeCode({ code: initCode });
|
|
41
|
+
}
|
|
42
|
+
await this.kernelExecutor.executeCode(
|
|
43
|
+
{ code: `${this._server_var}.reload(app)` },
|
|
44
|
+
true
|
|
45
|
+
);
|
|
54
46
|
}
|
|
55
|
-
|
|
56
|
-
private DASH_GET_RESPONSE_FUNCTION = '__jupyterpack_dash_get_response';
|
|
57
47
|
}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IBasePythonServer, JupyterPackFramework } from '../type';
|
|
2
2
|
import { DashServer } from './dash/dashServer';
|
|
3
3
|
import { KernelExecutor } from './kernelExecutor';
|
|
4
|
+
import { ShinyServer } from './shiny/shinyServer';
|
|
5
|
+
import { StarletteServer } from './starlette/starletteServer';
|
|
4
6
|
import { StreamlitServer } from './streamlit/streamlitServer';
|
|
5
7
|
import { TornadoServer } from './tornado/tornadoServer';
|
|
6
8
|
|
|
7
|
-
type
|
|
9
|
+
type BasePythonServerConstructor = new (
|
|
8
10
|
options: KernelExecutor.IOptions
|
|
9
|
-
) =>
|
|
11
|
+
) => IBasePythonServer;
|
|
10
12
|
|
|
11
13
|
export const PYTHON_SERVER = new Map<
|
|
12
14
|
JupyterPackFramework,
|
|
13
|
-
|
|
15
|
+
BasePythonServerConstructor
|
|
14
16
|
>([
|
|
15
17
|
[JupyterPackFramework.DASH, DashServer],
|
|
16
18
|
[JupyterPackFramework.STREAMLIT, StreamlitServer],
|
|
17
|
-
[JupyterPackFramework.TORNADO, TornadoServer]
|
|
19
|
+
[JupyterPackFramework.TORNADO, TornadoServer],
|
|
20
|
+
[JupyterPackFramework.SHINY, ShinyServer],
|
|
21
|
+
[JupyterPackFramework.STARLETTE, StarletteServer]
|
|
18
22
|
]);
|