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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/lib/document/widgetFactory.js +3 -1
  3. package/lib/pythonServer/baseServer.d.ts +82 -0
  4. package/lib/pythonServer/baseServer.js +141 -0
  5. package/lib/pythonServer/dash/dashServer.d.ts +2 -12
  6. package/lib/pythonServer/dash/dashServer.js +8 -21
  7. package/lib/pythonServer/index.d.ts +3 -3
  8. package/lib/pythonServer/index.js +5 -1
  9. package/lib/pythonServer/kernelExecutor.d.ts +2 -58
  10. package/lib/pythonServer/kernelExecutor.js +0 -72
  11. package/lib/pythonServer/shiny/shinyServer.d.ts +14 -0
  12. package/lib/pythonServer/shiny/shinyServer.js +49 -0
  13. package/lib/pythonServer/starlette/starletteServer.d.ts +13 -0
  14. package/lib/pythonServer/starlette/starletteServer.js +49 -0
  15. package/lib/pythonServer/streamlit/streamlitServer.d.ts +0 -1
  16. package/lib/pythonServer/streamlit/streamlitServer.js +7 -11
  17. package/lib/pythonServer/tornado/tornadoServer.d.ts +2 -24
  18. package/lib/pythonServer/tornado/tornadoServer.js +10 -35
  19. package/lib/swConnection/mainConnectionManager.d.ts +4 -4
  20. package/lib/swConnection/mainConnectionManager.js +23 -12
  21. package/lib/tools.d.ts +1 -0
  22. package/lib/tools.js +8 -0
  23. package/lib/type.d.ts +12 -3
  24. package/lib/type.js +2 -0
  25. package/lib/websocket/websocket.js +5 -1
  26. package/package.json +1 -1
  27. package/src/document/widgetFactory.ts +3 -1
  28. package/src/pythonServer/baseServer.ts +245 -0
  29. package/src/pythonServer/dash/dashServer.ts +10 -29
  30. package/src/pythonServer/index.ts +9 -5
  31. package/src/pythonServer/kernelExecutor.ts +3 -156
  32. package/src/pythonServer/shiny/shinyServer.ts +62 -0
  33. package/src/pythonServer/starlette/starletteServer.ts +59 -0
  34. package/src/pythonServer/streamlit/streamlitServer.ts +7 -9
  35. package/src/pythonServer/tornado/tornadoServer.ts +11 -55
  36. package/src/pythonWidget/pythonWidgetModel.ts +2 -2
  37. package/src/swConnection/mainConnectionManager.ts +28 -20
  38. package/src/tools.ts +9 -0
  39. package/src/type.ts +17 -6
  40. package/src/websocket/websocket.ts +9 -1
@@ -1,8 +1,7 @@
1
- import { stringOrNone } from '../../tools';
2
- import { IDict, JupyterPackFramework } from '../../type';
3
- import { KernelExecutor } from '../kernelExecutor';
1
+ import { JupyterPackFramework } from '../../type';
2
+ import { BasePythonServer } from '../baseServer';
4
3
 
5
- export class DashServer extends KernelExecutor {
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._DASH_SERVER_VAR} = DashServer(app, "${baseURL}")
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._DASH_SERVER_VAR}.reload(app)` },
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 { IKernelExecutor, JupyterPackFramework } from '../type';
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 KernelExecutorConstructor = new (
9
+ type BasePythonServerConstructor = new (
8
10
  options: KernelExecutor.IOptions
9
- ) => IKernelExecutor;
11
+ ) => IBasePythonServer;
10
12
 
11
13
  export const PYTHON_SERVER = new Map<
12
14
  JupyterPackFramework,
13
- KernelExecutorConstructor
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
- export abstract class KernelExecutor implements IKernelExecutor {
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._SERVER_VAR} = StreamlitServer(__jupyterpack_tor_app, "${baseURL}", __jupyterpack_st_server)
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._SERVER_VAR}.dispose()
54
+ ${this._server_var}.dispose()
55
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)
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 { stringOrNone } from '../../tools';
2
- import { IDict, JupyterPackFramework } from '../../type';
3
- import { KernelExecutor } from '../kernelExecutor';
1
+ import { JupyterPackFramework } from '../../type';
2
+ import { BasePythonServer } from '../baseServer';
4
3
 
5
- export class TornadoServer extends KernelExecutor {
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._SERVER_VAR} = TornadoServer(app, "${baseURL}")
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._SERVER_VAR}.dispose()
90
- ${this._SERVER_VAR}.reload(app)
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?: IKernelExecutor;
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
- kernelExecutor: IKernelExecutor
22
+ pythonServer: IBasePythonServer
28
23
  ): Promise<{ instanceId: string; kernelClientId: string }> {
29
24
  const uuid = UUID.uuid4();
30
25
 
31
- this._kernelExecutors.set(uuid, kernelExecutor);
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._kernelExecutors.get(kernelClientId);
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
- this._wsBroadcastChannel.onmessage = event => {
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._kernelExecutors.get(dest);
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._kernelExecutors
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
- serializedData = payload;
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 _kernelExecutors = new Map<string, IKernelExecutor>();
120
- private _wsBroadcastChannel: BroadcastChannel;
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
  }