jupyterpack 0.4.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/widgetFactory.js +3 -1
- package/lib/pythonServer/baseServer.d.ts +82 -0
- package/lib/pythonServer/baseServer.js +141 -0
- package/lib/pythonServer/dash/dashServer.d.ts +2 -12
- package/lib/pythonServer/dash/dashServer.js +8 -21
- package/lib/pythonServer/index.d.ts +3 -3
- package/lib/pythonServer/index.js +5 -1
- package/lib/pythonServer/kernelExecutor.d.ts +2 -58
- package/lib/pythonServer/kernelExecutor.js +0 -72
- 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 +0 -1
- package/lib/pythonServer/streamlit/streamlitServer.js +7 -11
- package/lib/pythonServer/tornado/tornadoServer.d.ts +2 -24
- package/lib/pythonServer/tornado/tornadoServer.js +10 -35
- package/lib/swConnection/mainConnectionManager.d.ts +4 -4
- package/lib/swConnection/mainConnectionManager.js +23 -12
- package/lib/tools.d.ts +1 -0
- package/lib/tools.js +8 -0
- package/lib/type.d.ts +12 -3
- package/lib/type.js +2 -0
- package/lib/websocket/websocket.js +5 -1
- package/package.json +1 -1
- package/src/document/widgetFactory.ts +3 -1
- package/src/pythonServer/baseServer.ts +245 -0
- package/src/pythonServer/dash/dashServer.ts +10 -29
- package/src/pythonServer/index.ts +9 -5
- package/src/pythonServer/kernelExecutor.ts +3 -156
- package/src/pythonServer/shiny/shinyServer.ts +62 -0
- package/src/pythonServer/starlette/starletteServer.ts +59 -0
- package/src/pythonServer/streamlit/streamlitServer.ts +7 -9
- package/src/pythonServer/tornado/tornadoServer.ts +11 -55
- package/src/pythonWidget/pythonWidgetModel.ts +2 -2
- package/src/swConnection/mainConnectionManager.ts +28 -20
- package/src/tools.ts +9 -0
- package/src/type.ts +17 -6
- package/src/websocket/websocket.ts +9 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { KernelExecutor } from '../kernelExecutor';
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { BasePythonServer } from '../baseServer';
|
|
4
3
|
|
|
5
|
-
export class DashServer extends
|
|
4
|
+
export class DashServer extends BasePythonServer {
|
|
6
5
|
async init(options: {
|
|
7
6
|
initCode?: string;
|
|
8
7
|
instanceId: string;
|
|
@@ -16,36 +15,20 @@ export class DashServer extends KernelExecutor {
|
|
|
16
15
|
kernelClientId,
|
|
17
16
|
framework: JupyterPackFramework.DASH
|
|
18
17
|
});
|
|
19
|
-
await this.executeCode({
|
|
18
|
+
await this.kernelExecutor.executeCode({
|
|
20
19
|
code: `
|
|
21
20
|
import os
|
|
22
21
|
os.environ["DASH_URL_BASE_PATHNAME"] = "${baseURL}"
|
|
23
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 = `
|
|
29
28
|
from jupyterpack.dash import DashServer
|
|
30
|
-
${this.
|
|
29
|
+
${this._server_var} = DashServer(app, "${baseURL}")
|
|
31
30
|
`;
|
|
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()` });
|
|
31
|
+
await this.kernelExecutor.executeCode({ code: loaderCode });
|
|
49
32
|
}
|
|
50
33
|
|
|
51
34
|
async reloadPythonServer(options: {
|
|
@@ -54,13 +37,11 @@ export class DashServer extends KernelExecutor {
|
|
|
54
37
|
}): Promise<void> {
|
|
55
38
|
const { initCode } = options;
|
|
56
39
|
if (initCode) {
|
|
57
|
-
await this.executeCode({ code: initCode });
|
|
40
|
+
await this.kernelExecutor.executeCode({ code: initCode });
|
|
58
41
|
}
|
|
59
|
-
await this.executeCode(
|
|
60
|
-
{ code: `${this.
|
|
42
|
+
await this.kernelExecutor.executeCode(
|
|
43
|
+
{ code: `${this._server_var}.reload(app)` },
|
|
61
44
|
true
|
|
62
45
|
);
|
|
63
46
|
}
|
|
64
|
-
|
|
65
|
-
private _DASH_SERVER_VAR = '__jupyterpack_dash_server';
|
|
66
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
|
]);
|
|
@@ -1,146 +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
3
|
|
|
13
|
-
|
|
4
|
+
import { IKernelExecutor } from '../type';
|
|
5
|
+
|
|
6
|
+
export class KernelExecutor implements IKernelExecutor {
|
|
14
7
|
constructor(options: KernelExecutor.IOptions) {
|
|
15
8
|
this._sessionConnection = options.sessionConnection;
|
|
16
|
-
this._wsPatch = websocketPatch.replaceAll('"use strict";', '');
|
|
17
9
|
}
|
|
18
10
|
|
|
19
11
|
get isDisposed(): boolean {
|
|
20
12
|
return this._isDisposed;
|
|
21
13
|
}
|
|
22
14
|
|
|
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
15
|
async executeCode(
|
|
145
16
|
code: KernelMessage.IExecuteRequestMsg['content'],
|
|
146
17
|
waitForResult?: boolean
|
|
@@ -208,32 +79,8 @@ export abstract class KernelExecutor implements IKernelExecutor {
|
|
|
208
79
|
this._sessionConnection.dispose();
|
|
209
80
|
}
|
|
210
81
|
|
|
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
82
|
private _isDisposed: boolean = false;
|
|
235
83
|
private _sessionConnection: Session.ISessionConnection;
|
|
236
|
-
private _wsPatch: string;
|
|
237
84
|
}
|
|
238
85
|
|
|
239
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
|
+
}
|
|
@@ -26,20 +26,20 @@ export class StreamlitServer extends TornadoServer {
|
|
|
26
26
|
patch_tornado()
|
|
27
27
|
set_base_url_env("${baseURL}")
|
|
28
28
|
`;
|
|
29
|
-
await this.executeCode({ code: patchCode });
|
|
29
|
+
await this.kernelExecutor.executeCode({ code: patchCode });
|
|
30
30
|
|
|
31
31
|
const bootstrapCode = `
|
|
32
32
|
from jupyterpack.streamlit import patch_streamlit
|
|
33
33
|
patch_streamlit()
|
|
34
34
|
`;
|
|
35
|
-
await this.executeCode({ code: bootstrapCode });
|
|
35
|
+
await this.kernelExecutor.executeCode({ code: bootstrapCode });
|
|
36
36
|
|
|
37
37
|
const stCode = `
|
|
38
38
|
from jupyterpack.streamlit import StreamlitServer, create_streamlit_app
|
|
39
39
|
__jupyterpack_st_server, __jupyterpack_tor_app = await create_streamlit_app("${entryPath}", "${baseURL}")
|
|
40
|
-
${this.
|
|
40
|
+
${this._server_var} = StreamlitServer(__jupyterpack_tor_app, "${baseURL}", __jupyterpack_st_server)
|
|
41
41
|
`;
|
|
42
|
-
await this.executeCode({ code: stCode });
|
|
42
|
+
await this.kernelExecutor.executeCode({ code: stCode });
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
async reloadPythonServer(options: {
|
|
@@ -51,17 +51,15 @@ export class StreamlitServer extends TornadoServer {
|
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
const reloadCode = `
|
|
54
|
-
${this.
|
|
54
|
+
${this._server_var}.dispose()
|
|
55
55
|
__jupyterpack_st_server, __jupyterpack_tor_app = await create_streamlit_app("${entryPath}", "${this._baseUrl}")
|
|
56
|
-
${this.
|
|
56
|
+
${this._server_var}.reload(__jupyterpack_tor_app, __jupyterpack_st_server)
|
|
57
57
|
`;
|
|
58
|
-
await this.executeCode(
|
|
58
|
+
await this.kernelExecutor.executeCode(
|
|
59
59
|
{
|
|
60
60
|
code: reloadCode
|
|
61
61
|
},
|
|
62
62
|
true
|
|
63
63
|
);
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
protected _SERVER_VAR = '__jupyterpack_streamlit_server';
|
|
67
65
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { KernelExecutor } from '../kernelExecutor';
|
|
1
|
+
import { JupyterPackFramework } from '../../type';
|
|
2
|
+
import { BasePythonServer } from '../baseServer';
|
|
4
3
|
|
|
5
|
-
export class TornadoServer extends
|
|
4
|
+
export class TornadoServer extends BasePythonServer {
|
|
6
5
|
async init(options: {
|
|
7
6
|
initCode?: string;
|
|
8
7
|
instanceId: string;
|
|
@@ -22,76 +21,33 @@ export class TornadoServer extends KernelExecutor {
|
|
|
22
21
|
patch_tornado()
|
|
23
22
|
|
|
24
23
|
`;
|
|
25
|
-
await this.executeCode({ code: bootstrapCode });
|
|
24
|
+
await this.kernelExecutor.executeCode({ code: bootstrapCode });
|
|
26
25
|
if (initCode) {
|
|
27
26
|
const initCodeWithUrl = initCode.replaceAll('{{base_url}}', baseURL);
|
|
28
|
-
await this.executeCode({ code: initCodeWithUrl });
|
|
27
|
+
await this.kernelExecutor.executeCode({ code: initCodeWithUrl });
|
|
29
28
|
const loaderCode = `
|
|
30
29
|
from jupyterpack.tornado import TornadoServer
|
|
31
|
-
${this.
|
|
30
|
+
${this._server_var} = TornadoServer(app, "${baseURL}")
|
|
32
31
|
`;
|
|
33
32
|
|
|
34
|
-
await this.executeCode({ code: loaderCode });
|
|
33
|
+
await this.kernelExecutor.executeCode({ code: loaderCode });
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
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
37
|
async reloadPythonServer(options: {
|
|
80
38
|
entryPath?: string;
|
|
81
39
|
initCode?: string;
|
|
82
40
|
}): Promise<void> {
|
|
83
41
|
const { initCode } = options;
|
|
84
42
|
if (initCode) {
|
|
85
|
-
await this.executeCode({
|
|
43
|
+
await this.kernelExecutor.executeCode({
|
|
86
44
|
code: initCode.replaceAll('{{base_url}}', this._baseUrl ?? '')
|
|
87
45
|
});
|
|
88
46
|
const reloadCode = `
|
|
89
|
-
${this.
|
|
90
|
-
${this.
|
|
47
|
+
${this._server_var}.dispose()
|
|
48
|
+
${this._server_var}.reload(app)
|
|
91
49
|
`;
|
|
92
|
-
await this.executeCode({ code: reloadCode }, true);
|
|
50
|
+
await this.kernelExecutor.executeCode({ code: reloadCode }, true);
|
|
93
51
|
}
|
|
94
52
|
}
|
|
95
|
-
|
|
96
|
-
protected _SERVER_VAR = '__jupyterpack_tornado_server';
|
|
97
53
|
}
|
|
@@ -11,9 +11,9 @@ import { PromiseDelegate } from '@lumino/coreutils';
|
|
|
11
11
|
import { PYTHON_SERVER } from '../pythonServer';
|
|
12
12
|
import { Signal } from '@lumino/signaling';
|
|
13
13
|
import {
|
|
14
|
+
IBasePythonServer,
|
|
14
15
|
IConnectionManager,
|
|
15
16
|
IJupyterPackFileFormat,
|
|
16
|
-
IKernelExecutor,
|
|
17
17
|
IPythonWidgetModel,
|
|
18
18
|
JupyterPackFramework
|
|
19
19
|
} from '../type';
|
|
@@ -203,7 +203,7 @@ export class PythonWidgetModel implements IPythonWidgetModel {
|
|
|
203
203
|
private _connectionManager: IConnectionManager;
|
|
204
204
|
private _contentsManager: Contents.IManager;
|
|
205
205
|
private _jpackModel: IJupyterPackFileFormat;
|
|
206
|
-
private _executor?:
|
|
206
|
+
private _executor?: IBasePythonServer;
|
|
207
207
|
private _localPath: string;
|
|
208
208
|
|
|
209
209
|
private _serverReloaded: Signal<IPythonWidgetModel, void> = new Signal<
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { arrayBufferToBase64 } from '../tools';
|
|
1
|
+
import { arrayBufferToBase64, stringToBase64 } from '../tools';
|
|
2
2
|
import {
|
|
3
|
+
IBasePythonServer,
|
|
3
4
|
IBroadcastMessage,
|
|
4
5
|
IConnectionManager,
|
|
5
|
-
IDict
|
|
6
|
-
IKernelExecutor
|
|
6
|
+
IDict
|
|
7
7
|
} from '../type';
|
|
8
8
|
import { UUID } from '@lumino/coreutils';
|
|
9
9
|
|
|
@@ -16,20 +16,19 @@ import { UUID } from '@lumino/coreutils';
|
|
|
16
16
|
* It's running on the main thread
|
|
17
17
|
*/
|
|
18
18
|
export class ConnectionManager implements IConnectionManager {
|
|
19
|
-
constructor(public instanceId: string) {
|
|
20
|
-
this._wsBroadcastChannel = new BroadcastChannel(
|
|
21
|
-
`/jupyterpack/ws/${instanceId}`
|
|
22
|
-
);
|
|
23
|
-
this._initWsChannel();
|
|
24
|
-
}
|
|
19
|
+
constructor(public instanceId: string) {}
|
|
25
20
|
|
|
26
21
|
async registerConnection(
|
|
27
|
-
|
|
22
|
+
pythonServer: IBasePythonServer
|
|
28
23
|
): Promise<{ instanceId: string; kernelClientId: string }> {
|
|
29
24
|
const uuid = UUID.uuid4();
|
|
30
25
|
|
|
31
|
-
this.
|
|
32
|
-
|
|
26
|
+
this._pythonServers.set(uuid, pythonServer);
|
|
27
|
+
const wsbc = new BroadcastChannel(
|
|
28
|
+
`/jupyterpack/ws/${this.instanceId}/${uuid}`
|
|
29
|
+
);
|
|
30
|
+
this._initWsChannel(wsbc);
|
|
31
|
+
this._wsBroadcastChannelMap.set(`${this.instanceId}/${uuid}`, wsbc);
|
|
33
32
|
return { instanceId: this.instanceId, kernelClientId: uuid };
|
|
34
33
|
}
|
|
35
34
|
|
|
@@ -43,7 +42,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
43
42
|
}): Promise<IDict | null> {
|
|
44
43
|
const { urlPath, kernelClientId, method, params, requestBody, headers } =
|
|
45
44
|
options;
|
|
46
|
-
const executor = this.
|
|
45
|
+
const executor = this._pythonServers.get(kernelClientId);
|
|
47
46
|
if (!executor) {
|
|
48
47
|
return null;
|
|
49
48
|
}
|
|
@@ -57,8 +56,8 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
57
56
|
});
|
|
58
57
|
return response;
|
|
59
58
|
}
|
|
60
|
-
private _initWsChannel() {
|
|
61
|
-
|
|
59
|
+
private _initWsChannel(broadcastChannel: BroadcastChannel) {
|
|
60
|
+
broadcastChannel.onmessage = event => {
|
|
62
61
|
const rawData = event.data;
|
|
63
62
|
let data: IBroadcastMessage;
|
|
64
63
|
if (typeof rawData === 'string') {
|
|
@@ -68,13 +67,13 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
const { action, dest, wsUrl, payload } = data;
|
|
71
|
-
const executor = this.
|
|
70
|
+
const executor = this._pythonServers.get(dest);
|
|
72
71
|
if (!executor) {
|
|
73
72
|
console.error(
|
|
74
73
|
'Missing kernel handle for message',
|
|
75
74
|
data,
|
|
76
75
|
dest,
|
|
77
|
-
this.
|
|
76
|
+
this._pythonServers
|
|
78
77
|
);
|
|
79
78
|
return;
|
|
80
79
|
}
|
|
@@ -89,6 +88,14 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
89
88
|
});
|
|
90
89
|
break;
|
|
91
90
|
}
|
|
91
|
+
case 'close': {
|
|
92
|
+
executor.closeWebsocket({
|
|
93
|
+
instanceId: this.instanceId,
|
|
94
|
+
kernelId: dest,
|
|
95
|
+
wsUrl
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
92
99
|
case 'send': {
|
|
93
100
|
let serializedData: string;
|
|
94
101
|
let isBinary: boolean;
|
|
@@ -97,7 +104,8 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
97
104
|
serializedData = arrayBufferToBase64(payload as any);
|
|
98
105
|
isBinary = true;
|
|
99
106
|
} else if (typeof payload === 'string') {
|
|
100
|
-
|
|
107
|
+
// convert string to base64 string to avoid encoding problem
|
|
108
|
+
serializedData = stringToBase64(payload);
|
|
101
109
|
isBinary = false;
|
|
102
110
|
} else {
|
|
103
111
|
console.error('Unknown message type', payload);
|
|
@@ -116,6 +124,6 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
116
124
|
}
|
|
117
125
|
};
|
|
118
126
|
}
|
|
119
|
-
private
|
|
120
|
-
private
|
|
127
|
+
private _pythonServers = new Map<string, IBasePythonServer>();
|
|
128
|
+
private _wsBroadcastChannelMap: Map<string, BroadcastChannel> = new Map();
|
|
121
129
|
}
|
package/src/tools.ts
CHANGED
|
@@ -53,6 +53,15 @@ export function base64ToString(base64: string): string {
|
|
|
53
53
|
return new TextDecoder('utf-8').decode(bytes);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export function stringToBase64(str: string): string {
|
|
57
|
+
const bytes = new TextEncoder().encode(str);
|
|
58
|
+
let binary = '';
|
|
59
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
60
|
+
binary += String.fromCharCode(bytes[i]);
|
|
61
|
+
}
|
|
62
|
+
return window.btoa(binary);
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
export function stringOrNone(content?: string) {
|
|
57
66
|
return content ? `"${content}"` : 'None';
|
|
58
67
|
}
|