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
|
@@ -1,139 +1,17 @@
|
|
|
1
|
-
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
2
1
|
import { KernelMessage, Session } from '@jupyterlab/services';
|
|
3
2
|
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
|
-
import { patch } from './common/generatedPythonFiles';
|
|
13
3
|
|
|
14
|
-
|
|
4
|
+
import { IKernelExecutor } from '../type';
|
|
5
|
+
|
|
6
|
+
export class KernelExecutor implements IKernelExecutor {
|
|
15
7
|
constructor(options: KernelExecutor.IOptions) {
|
|
16
8
|
this._sessionConnection = options.sessionConnection;
|
|
17
|
-
this._wsPatch = websocketPatch.replaceAll('"use strict";', '');
|
|
18
9
|
}
|
|
19
10
|
|
|
20
11
|
get isDisposed(): boolean {
|
|
21
12
|
return this._isDisposed;
|
|
22
13
|
}
|
|
23
14
|
|
|
24
|
-
abstract disposePythonServer(): Promise<void>;
|
|
25
|
-
|
|
26
|
-
abstract getResponseFunctionFactory(options: {
|
|
27
|
-
urlPath: string;
|
|
28
|
-
method: string;
|
|
29
|
-
headers: IDict;
|
|
30
|
-
params?: string;
|
|
31
|
-
content?: string;
|
|
32
|
-
}): string;
|
|
33
|
-
|
|
34
|
-
async init(options: {
|
|
35
|
-
entryPath?: string;
|
|
36
|
-
initCode?: string;
|
|
37
|
-
instanceId: string;
|
|
38
|
-
kernelClientId: string;
|
|
39
|
-
}): Promise<void> {
|
|
40
|
-
await this.executeCode({ code: patch });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
openWebsocketFunctionFactory(options: {
|
|
44
|
-
instanceId: string;
|
|
45
|
-
kernelId: string;
|
|
46
|
-
wsUrl: string;
|
|
47
|
-
protocol?: string;
|
|
48
|
-
}): string | undefined {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
sendWebsocketMessageFunctionFactory(options: {
|
|
53
|
-
instanceId: string;
|
|
54
|
-
kernelId: string;
|
|
55
|
-
wsUrl: string;
|
|
56
|
-
message: string;
|
|
57
|
-
}): string | undefined {
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async openWebsocket(options: {
|
|
62
|
-
instanceId: string;
|
|
63
|
-
kernelId: string;
|
|
64
|
-
wsUrl: string;
|
|
65
|
-
protocol?: string;
|
|
66
|
-
}): Promise<void> {
|
|
67
|
-
const code = this.openWebsocketFunctionFactory(options);
|
|
68
|
-
if (code) {
|
|
69
|
-
await this.executeCode({ code });
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async sendWebsocketMessage(options: {
|
|
74
|
-
instanceId: string;
|
|
75
|
-
kernelId: string;
|
|
76
|
-
wsUrl: string;
|
|
77
|
-
message: string;
|
|
78
|
-
}): Promise<void> {
|
|
79
|
-
const code = this.sendWebsocketMessageFunctionFactory(options);
|
|
80
|
-
if (code) {
|
|
81
|
-
await this.executeCode({ code });
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async getResponse(options: {
|
|
86
|
-
method: string;
|
|
87
|
-
urlPath: string;
|
|
88
|
-
headers: IDict;
|
|
89
|
-
requestBody?: ArrayBuffer;
|
|
90
|
-
params?: string;
|
|
91
|
-
}): Promise<IDict> {
|
|
92
|
-
const { method, urlPath, requestBody, params, headers } = options;
|
|
93
|
-
const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
|
|
94
|
-
const code = this.getResponseFunctionFactory({
|
|
95
|
-
method,
|
|
96
|
-
urlPath,
|
|
97
|
-
headers,
|
|
98
|
-
params,
|
|
99
|
-
content
|
|
100
|
-
});
|
|
101
|
-
const raw = await this.executeCode({ code }, true);
|
|
102
|
-
if (!raw) {
|
|
103
|
-
throw new Error(`Missing response for ${urlPath}`);
|
|
104
|
-
}
|
|
105
|
-
const jsonStr = raw.replaceAll("'", '');
|
|
106
|
-
const obj: {
|
|
107
|
-
headers: string;
|
|
108
|
-
status_code: number;
|
|
109
|
-
content: string;
|
|
110
|
-
} = JSON.parse(jsonStr);
|
|
111
|
-
const responseHeaders: IDict<string> = JSON.parse(atob(obj.headers));
|
|
112
|
-
const contentType: string | undefined =
|
|
113
|
-
responseHeaders?.['Content-Type'] ?? responseHeaders?.['content-type'];
|
|
114
|
-
let responseContent: string | Uint8Array;
|
|
115
|
-
|
|
116
|
-
if (isBinaryContentType(contentType)) {
|
|
117
|
-
responseContent = base64ToArrayBuffer(obj.content);
|
|
118
|
-
} else {
|
|
119
|
-
responseContent = base64ToString(obj.content);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (contentType && contentType.toLowerCase() === 'text/html') {
|
|
123
|
-
responseContent = (responseContent as string).replace(
|
|
124
|
-
'<head>',
|
|
125
|
-
`<head>\n<script>\n${this._wsPatch}\n</script>\n`
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const decodedObj = {
|
|
130
|
-
status_code: obj.status_code,
|
|
131
|
-
headers: responseHeaders,
|
|
132
|
-
content: responseContent
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
return decodedObj;
|
|
136
|
-
}
|
|
137
15
|
async executeCode(
|
|
138
16
|
code: KernelMessage.IExecuteRequestMsg['content'],
|
|
139
17
|
waitForResult?: boolean
|
|
@@ -160,10 +38,13 @@ export abstract class KernelExecutor implements IKernelExecutor {
|
|
|
160
38
|
}
|
|
161
39
|
case 'stream': {
|
|
162
40
|
const content = (msg as KernelMessage.IStreamMsg).content;
|
|
41
|
+
if (content.text.length === 0) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
163
44
|
if (content.name === 'stderr') {
|
|
164
|
-
console.error('Kernel stream', content.text);
|
|
45
|
+
console.error('Kernel stream:', content.text);
|
|
165
46
|
} else {
|
|
166
|
-
console.log('Kernel stream', content.text);
|
|
47
|
+
console.log('Kernel stream:', content.text);
|
|
167
48
|
}
|
|
168
49
|
break;
|
|
169
50
|
}
|
|
@@ -198,28 +79,8 @@ export abstract class KernelExecutor implements IKernelExecutor {
|
|
|
198
79
|
this._sessionConnection.dispose();
|
|
199
80
|
}
|
|
200
81
|
|
|
201
|
-
protected buildBaseURL(options: {
|
|
202
|
-
instanceId: string;
|
|
203
|
-
kernelClientId: string;
|
|
204
|
-
framework: JupyterPackFramework;
|
|
205
|
-
}) {
|
|
206
|
-
const { instanceId, kernelClientId, framework } = options;
|
|
207
|
-
const labBaseUrl = PageConfig.getOption('baseUrl');
|
|
208
|
-
const baseURL = URLExt.join(
|
|
209
|
-
labBaseUrl,
|
|
210
|
-
'extensions/jupyterpack/static',
|
|
211
|
-
instanceId,
|
|
212
|
-
framework,
|
|
213
|
-
kernelClientId,
|
|
214
|
-
'/'
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
return baseURL;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
82
|
private _isDisposed: boolean = false;
|
|
221
83
|
private _sessionConnection: Session.ISessionConnection;
|
|
222
|
-
private _wsPatch: string;
|
|
223
84
|
}
|
|
224
85
|
|
|
225
86
|
export namespace KernelExecutor {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { BasePythonServer } from '../baseServer';
|
|
3
|
+
|
|
4
|
+
export class ShinyServer extends BasePythonServer {
|
|
5
|
+
async init(options: {
|
|
6
|
+
entryPath?: string;
|
|
7
|
+
initCode?: string;
|
|
8
|
+
instanceId: string;
|
|
9
|
+
kernelClientId: string;
|
|
10
|
+
}) {
|
|
11
|
+
await super.init(options);
|
|
12
|
+
const { instanceId, kernelClientId, entryPath } = options;
|
|
13
|
+
const baseURL = this.buildBaseURL({
|
|
14
|
+
instanceId,
|
|
15
|
+
kernelClientId,
|
|
16
|
+
framework: JupyterPackFramework.SHINY
|
|
17
|
+
});
|
|
18
|
+
const bootstrapCode = `
|
|
19
|
+
from jupyterpack.common import set_base_url_env
|
|
20
|
+
set_base_url_env("${baseURL}")
|
|
21
|
+
from jupyterpack.shiny import patch_shiny
|
|
22
|
+
patch_shiny()
|
|
23
|
+
`;
|
|
24
|
+
await this.kernelExecutor.executeCode({ code: bootstrapCode });
|
|
25
|
+
if (entryPath) {
|
|
26
|
+
const loaderCode = `
|
|
27
|
+
from jupyterpack.shiny import ShinyServer, get_shiny_app
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
${this._server_var} = ShinyServer(get_shiny_app("${entryPath}"), "${baseURL}")
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
await this.kernelExecutor.executeCode({ code: loaderCode });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async disposePythonServer(): Promise<void> {
|
|
38
|
+
await this.kernelExecutor.executeCode({
|
|
39
|
+
code: `${this._server_var}.dispose()`
|
|
40
|
+
});
|
|
41
|
+
for (const element of this._openedWebsockets) {
|
|
42
|
+
await this.closeWebsocket(element);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async reloadPythonServer(options: {
|
|
47
|
+
entryPath?: string;
|
|
48
|
+
initCode?: string;
|
|
49
|
+
}): Promise<void> {
|
|
50
|
+
const { entryPath } = options;
|
|
51
|
+
if (entryPath) {
|
|
52
|
+
const reloadCode = `
|
|
53
|
+
from jupyterpack.shiny import get_shiny_app
|
|
54
|
+
|
|
55
|
+
await ${this._server_var}.dispose()
|
|
56
|
+
${this._server_var}.reload(get_shiny_app("${entryPath}"))
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
await this.kernelExecutor.executeCode({ code: reloadCode }, true);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { BasePythonServer } from '../baseServer';
|
|
3
|
+
|
|
4
|
+
export class StarletteServer extends BasePythonServer {
|
|
5
|
+
async init(options: {
|
|
6
|
+
initCode?: string;
|
|
7
|
+
instanceId: string;
|
|
8
|
+
kernelClientId: string;
|
|
9
|
+
}) {
|
|
10
|
+
await super.init(options);
|
|
11
|
+
const { initCode, instanceId, kernelClientId } = options;
|
|
12
|
+
const baseURL = this.buildBaseURL({
|
|
13
|
+
instanceId,
|
|
14
|
+
kernelClientId,
|
|
15
|
+
framework: JupyterPackFramework.STARLETTE
|
|
16
|
+
});
|
|
17
|
+
const bootstrapCode = `
|
|
18
|
+
from jupyterpack.common import set_base_url_env
|
|
19
|
+
set_base_url_env("${baseURL}")
|
|
20
|
+
`;
|
|
21
|
+
await this.kernelExecutor.executeCode({ code: bootstrapCode });
|
|
22
|
+
if (initCode) {
|
|
23
|
+
const initCodeWithUrl = initCode.replaceAll('{{base_url}}', baseURL);
|
|
24
|
+
await this.kernelExecutor.executeCode({ code: initCodeWithUrl });
|
|
25
|
+
const loaderCode = `
|
|
26
|
+
from jupyterpack.asgi import AsgiServer
|
|
27
|
+
${this._server_var} = AsgiServer(app, "${baseURL}")
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
await this.kernelExecutor.executeCode({ code: loaderCode });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async disposePythonServer(): Promise<void> {
|
|
35
|
+
await this.kernelExecutor.executeCode({
|
|
36
|
+
code: `${this._server_var}.dispose()`
|
|
37
|
+
});
|
|
38
|
+
for (const element of this._openedWebsockets) {
|
|
39
|
+
await this.closeWebsocket(element);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async reloadPythonServer(options: {
|
|
44
|
+
entryPath?: string;
|
|
45
|
+
initCode?: string;
|
|
46
|
+
}): Promise<void> {
|
|
47
|
+
const { initCode } = options;
|
|
48
|
+
if (initCode) {
|
|
49
|
+
await this.kernelExecutor.executeCode({
|
|
50
|
+
code: initCode.replaceAll('{{base_url}}', this._baseUrl ?? '')
|
|
51
|
+
});
|
|
52
|
+
const reloadCode = `
|
|
53
|
+
await ${this._server_var}.dispose()
|
|
54
|
+
${this._server_var}.reload(app)
|
|
55
|
+
`;
|
|
56
|
+
await this.kernelExecutor.executeCode({ code: reloadCode }, true);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { patch, tools } from '../common/generatedPythonFiles';
|
|
4
|
-
import { KernelExecutor } from '../kernelExecutor';
|
|
5
|
-
import {
|
|
6
|
-
bootstrap as tornadoBootstrap,
|
|
7
|
-
tornadoBridge
|
|
8
|
-
} from '../tornado/generatedPythonFiles';
|
|
9
|
-
import { bootstrap, streamlitLoader } from './generatedPythonFiles';
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { TornadoServer } from '../tornado/tornadoServer';
|
|
10
3
|
|
|
11
|
-
export class StreamlitServer extends
|
|
4
|
+
export class StreamlitServer extends TornadoServer {
|
|
12
5
|
async init(options: {
|
|
13
6
|
entryPath?: string;
|
|
14
7
|
initCode?: string;
|
|
15
8
|
instanceId: string;
|
|
16
9
|
kernelClientId: string;
|
|
17
10
|
}) {
|
|
18
|
-
await super.init(options);
|
|
19
11
|
const { instanceId, kernelClientId, entryPath } = options;
|
|
20
12
|
if (!entryPath) {
|
|
21
13
|
throw new Error(
|
|
@@ -27,61 +19,47 @@ export class StreamlitServer extends KernelExecutor {
|
|
|
27
19
|
kernelClientId,
|
|
28
20
|
framework: JupyterPackFramework.STREAMLIT
|
|
29
21
|
});
|
|
30
|
-
await this.executeCode({ code: patch });
|
|
31
|
-
await this.executeCode({ code: tools.replaceAll('{{base_url}}', baseURL) });
|
|
32
|
-
await this.executeCode({ code: tornadoBootstrap });
|
|
33
|
-
await this.executeCode({ code: tornadoBridge });
|
|
34
|
-
await this.executeCode({ code: bootstrap });
|
|
35
22
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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.kernelExecutor.executeCode({ code: patchCode });
|
|
41
30
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
content?: string;
|
|
48
|
-
}) {
|
|
49
|
-
const { method, urlPath, headers, params, content } = options;
|
|
50
|
-
const code = `await ${this._GET_RESPONSE_FUNCTION}("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
|
|
51
|
-
return code;
|
|
52
|
-
}
|
|
31
|
+
const bootstrapCode = `
|
|
32
|
+
from jupyterpack.streamlit import patch_streamlit
|
|
33
|
+
patch_streamlit()
|
|
34
|
+
`;
|
|
35
|
+
await this.kernelExecutor.executeCode({ code: bootstrapCode });
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const { instanceId, kernelId, wsUrl, protocol } = options;
|
|
61
|
-
|
|
62
|
-
const code = `await ${this._OPEN_WEBSOCKET_FUNCTION}("${instanceId}", "${kernelId}", "${wsUrl}", ${stringOrNone(protocol)})`;
|
|
63
|
-
return code;
|
|
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.kernelExecutor.executeCode({ code: stCode });
|
|
64
43
|
}
|
|
65
44
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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.kernelExecutor.executeCode(
|
|
59
|
+
{
|
|
60
|
+
code: reloadCode
|
|
61
|
+
},
|
|
62
|
+
true
|
|
63
|
+
);
|
|
81
64
|
}
|
|
82
|
-
|
|
83
|
-
private _GET_RESPONSE_FUNCTION = '__jupyterpack_streamlit_get_response';
|
|
84
|
-
private _OPEN_WEBSOCKET_FUNCTION = '__jupyterpack_streamlit_open_ws';
|
|
85
|
-
private _SEND_WEBSOCKET_FUNCTION =
|
|
86
|
-
'__jupyterpack_streamlit_receive_ws_message';
|
|
87
65
|
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
bootstrap,
|
|
7
|
-
tornadoBridge,
|
|
8
|
-
tornadoLoader
|
|
9
|
-
} from './generatedPythonFiles';
|
|
10
|
-
export class TornadoServer extends KernelExecutor {
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { BasePythonServer } from '../baseServer';
|
|
3
|
+
|
|
4
|
+
export class TornadoServer extends BasePythonServer {
|
|
11
5
|
async init(options: {
|
|
12
6
|
initCode?: string;
|
|
13
7
|
instanceId: string;
|
|
@@ -21,60 +15,39 @@ export class TornadoServer extends KernelExecutor {
|
|
|
21
15
|
kernelClientId,
|
|
22
16
|
framework: JupyterPackFramework.TORNADO
|
|
23
17
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
const bootstrapCode = `
|
|
19
|
+
from jupyterpack.common import set_base_url_env, patch_tornado
|
|
20
|
+
set_base_url_env("${baseURL}")
|
|
21
|
+
patch_tornado()
|
|
22
|
+
|
|
23
|
+
`;
|
|
24
|
+
await this.kernelExecutor.executeCode({ code: bootstrapCode });
|
|
27
25
|
if (initCode) {
|
|
28
26
|
const initCodeWithUrl = initCode.replaceAll('{{base_url}}', baseURL);
|
|
29
|
-
await this.executeCode({ code: initCodeWithUrl });
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
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 = `await ${this._GET_RESPONSE_FUNCTION}("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
|
|
44
|
-
return code;
|
|
45
|
-
}
|
|
27
|
+
await this.kernelExecutor.executeCode({ code: initCodeWithUrl });
|
|
28
|
+
const loaderCode = `
|
|
29
|
+
from jupyterpack.tornado import TornadoServer
|
|
30
|
+
${this._server_var} = TornadoServer(app, "${baseURL}")
|
|
31
|
+
`;
|
|
46
32
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
kernelId: string;
|
|
50
|
-
wsUrl: string;
|
|
51
|
-
protocol?: string;
|
|
52
|
-
}): string {
|
|
53
|
-
const { instanceId, kernelId, wsUrl, protocol } = options;
|
|
54
|
-
|
|
55
|
-
const code = `await ${this._OPEN_WEBSOCKET_FUNCTION}("${instanceId}", "${kernelId}", "${wsUrl}", ${stringOrNone(protocol)})`;
|
|
56
|
-
return code;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
sendWebsocketMessageFunctionFactory(options: {
|
|
60
|
-
instanceId: string;
|
|
61
|
-
kernelId: string;
|
|
62
|
-
wsUrl: string;
|
|
63
|
-
message: string;
|
|
64
|
-
}): string {
|
|
65
|
-
const { instanceId, kernelId, wsUrl, message } = options;
|
|
66
|
-
const code = `await ${this._SEND_WEBSOCKET_FUNCTION}("${instanceId}", "${kernelId}", "${wsUrl}", '''${message}''')`;
|
|
67
|
-
return code;
|
|
33
|
+
await this.kernelExecutor.executeCode({ code: loaderCode });
|
|
34
|
+
}
|
|
68
35
|
}
|
|
69
36
|
|
|
70
|
-
async
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
37
|
+
async reloadPythonServer(options: {
|
|
38
|
+
entryPath?: string;
|
|
39
|
+
initCode?: string;
|
|
40
|
+
}): Promise<void> {
|
|
41
|
+
const { initCode } = options;
|
|
42
|
+
if (initCode) {
|
|
43
|
+
await this.kernelExecutor.executeCode({
|
|
44
|
+
code: initCode.replaceAll('{{base_url}}', this._baseUrl ?? '')
|
|
45
|
+
});
|
|
46
|
+
const reloadCode = `
|
|
47
|
+
${this._server_var}.dispose()
|
|
48
|
+
${this._server_var}.reload(app)
|
|
49
|
+
`;
|
|
50
|
+
await this.kernelExecutor.executeCode({ code: reloadCode }, true);
|
|
51
|
+
}
|
|
74
52
|
}
|
|
75
|
-
|
|
76
|
-
private _GET_RESPONSE_FUNCTION = '__jupyterpack_tornado_get_response';
|
|
77
|
-
private _OPEN_WEBSOCKET_FUNCTION = '__jupyterpack_tornado_open_ws';
|
|
78
|
-
|
|
79
|
-
private _SEND_WEBSOCKET_FUNCTION = '__jupyterpack_tornado_receive_ws_message';
|
|
80
53
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Kernel, KernelMessage } from '@jupyterlab/services';
|
|
2
|
+
|
|
3
|
+
const COMM_NAME = 'jupyterpack:broadcast:comm';
|
|
4
|
+
|
|
5
|
+
export class CommBroadcastManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this._kernels = new Map();
|
|
8
|
+
this._comms = new Map();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
registerKernel(kernel: Kernel.IKernelConnection) {
|
|
12
|
+
this._kernels.set(kernel.id, kernel);
|
|
13
|
+
kernel.registerCommTarget(COMM_NAME, (comm, msg) =>
|
|
14
|
+
this._handle_comm_open(comm, msg, kernel.id)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
unregisterKernel(kernelId?: string) {
|
|
18
|
+
if (kernelId) {
|
|
19
|
+
this._kernels.delete(kernelId);
|
|
20
|
+
const comms = this._comms.get(kernelId) ?? [];
|
|
21
|
+
comms.forEach(comm => comm.dispose());
|
|
22
|
+
this._comms.delete(kernelId);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private _handle_comm_open = async (
|
|
27
|
+
comm: Kernel.IComm,
|
|
28
|
+
msg: KernelMessage.ICommOpenMsg,
|
|
29
|
+
kernelId: string
|
|
30
|
+
): Promise<void> => {
|
|
31
|
+
if (this._comms.has(kernelId)) {
|
|
32
|
+
this._comms.get(kernelId)?.push(comm);
|
|
33
|
+
} else {
|
|
34
|
+
this._comms.set(kernelId, [comm]);
|
|
35
|
+
}
|
|
36
|
+
const channelName = msg.metadata.channel_name as string | undefined;
|
|
37
|
+
if (!channelName) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!this._broadcastChannels.has(channelName)) {
|
|
41
|
+
this._broadcastChannels.set(
|
|
42
|
+
channelName,
|
|
43
|
+
new BroadcastChannel(channelName)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
const broadcastChannel = this._broadcastChannels.get(channelName)!;
|
|
47
|
+
comm.onMsg = commMsg => {
|
|
48
|
+
const { data } = commMsg.content;
|
|
49
|
+
broadcastChannel.postMessage(data);
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
dispose() {
|
|
54
|
+
this._kernels.clear();
|
|
55
|
+
this._comms.clear();
|
|
56
|
+
this._broadcastChannels.forEach(it => {
|
|
57
|
+
it.close();
|
|
58
|
+
});
|
|
59
|
+
this._broadcastChannels.clear();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private _kernels: Map<string, Kernel.IKernelConnection> = new Map();
|
|
63
|
+
private _comms: Map<string, Kernel.IComm[]> = new Map();
|
|
64
|
+
private _broadcastChannels: Map<string, BroadcastChannel> = new Map();
|
|
65
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PythonWidgetModel } from './pythonWidgetModel';
|
|
2
2
|
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
3
3
|
import { IFramePanel } from '../document/iframePanel';
|
|
4
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
4
5
|
|
|
5
6
|
export class PythonWidget extends IFramePanel {
|
|
6
7
|
constructor(options: PythonWidget.IOptions) {
|
|
@@ -12,6 +13,7 @@ export class PythonWidget extends IFramePanel {
|
|
|
12
13
|
this._iframe.contentDocument!.body.innerText = `Failed to start server: ${connectionData.error}`;
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
16
|
+
this._isReady.resolve();
|
|
15
17
|
const iframe = this._iframe;
|
|
16
18
|
const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
|
|
17
19
|
|
|
@@ -23,24 +25,40 @@ export class PythonWidget extends IFramePanel {
|
|
|
23
25
|
connectionData.kernelClientId,
|
|
24
26
|
connectionData.rootUrl
|
|
25
27
|
);
|
|
26
|
-
|
|
27
28
|
iframe.src = iframeUrl;
|
|
28
29
|
|
|
29
30
|
iframe.addEventListener('load', () => {
|
|
30
31
|
this.toggleSpinner(false);
|
|
31
32
|
});
|
|
33
|
+
this._model.serverReloaded.connect(() => {
|
|
34
|
+
this._iframe?.contentWindow?.location?.reload();
|
|
35
|
+
});
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
get autoreload() {
|
|
40
|
+
return this._model.autoreload;
|
|
41
|
+
}
|
|
42
|
+
set autoreload(value: boolean) {
|
|
43
|
+
this._model.autoreload = value;
|
|
44
|
+
}
|
|
45
|
+
get isReady(): Promise<void> {
|
|
46
|
+
return this._isReady.promise;
|
|
47
|
+
}
|
|
35
48
|
get model(): PythonWidgetModel {
|
|
36
49
|
return this._model;
|
|
37
50
|
}
|
|
38
51
|
|
|
52
|
+
async reload(): Promise<void> {
|
|
53
|
+
await this._model.reload();
|
|
54
|
+
}
|
|
55
|
+
|
|
39
56
|
dispose(): void {
|
|
40
57
|
this._model.dispose();
|
|
41
58
|
}
|
|
42
59
|
|
|
43
60
|
private _model: PythonWidgetModel;
|
|
61
|
+
private _isReady = new PromiseDelegate<void>();
|
|
44
62
|
}
|
|
45
63
|
|
|
46
64
|
export namespace PythonWidget {
|