jupyterpack 0.4.0 → 0.5.2

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 (50) hide show
  1. package/README.md +107 -3
  2. package/lib/document/widgetFactory.js +3 -1
  3. package/lib/pythonServer/baseServer.d.ts +78 -0
  4. package/lib/pythonServer/baseServer.js +175 -0
  5. package/lib/pythonServer/dash/dashServer.d.ts +4 -17
  6. package/lib/pythonServer/dash/dashServer.js +14 -22
  7. package/lib/pythonServer/dash/deps.d.ts +2 -0
  8. package/lib/pythonServer/dash/deps.js +4 -0
  9. package/lib/pythonServer/index.d.ts +3 -3
  10. package/lib/pythonServer/index.js +5 -1
  11. package/lib/pythonServer/kernelExecutor.d.ts +2 -58
  12. package/lib/pythonServer/kernelExecutor.js +0 -72
  13. package/lib/pythonServer/shiny/deps.d.ts +2 -0
  14. package/lib/pythonServer/shiny/deps.js +4 -0
  15. package/lib/pythonServer/shiny/shinyServer.d.ts +10 -0
  16. package/lib/pythonServer/shiny/shinyServer.js +54 -0
  17. package/lib/pythonServer/starlette/starletteServer.d.ts +13 -0
  18. package/lib/pythonServer/starlette/starletteServer.js +49 -0
  19. package/lib/pythonServer/streamlit/deps.d.ts +2 -0
  20. package/lib/pythonServer/streamlit/deps.js +10 -0
  21. package/lib/pythonServer/streamlit/streamlitServer.d.ts +4 -9
  22. package/lib/pythonServer/streamlit/streamlitServer.js +16 -15
  23. package/lib/pythonServer/tornado/tornadoServer.d.ts +2 -24
  24. package/lib/pythonServer/tornado/tornadoServer.js +10 -35
  25. package/lib/pythonWidget/pythonWidgetModel.js +2 -0
  26. package/lib/swConnection/mainConnectionManager.d.ts +4 -4
  27. package/lib/swConnection/mainConnectionManager.js +23 -12
  28. package/lib/tools.d.ts +1 -0
  29. package/lib/tools.js +8 -0
  30. package/lib/type.d.ts +27 -9
  31. package/lib/type.js +2 -0
  32. package/lib/websocket/websocket.js +5 -1
  33. package/package.json +1 -1
  34. package/src/document/widgetFactory.ts +3 -1
  35. package/src/pythonServer/baseServer.ts +285 -0
  36. package/src/pythonServer/dash/dashServer.ts +18 -35
  37. package/src/pythonServer/dash/deps.ts +6 -0
  38. package/src/pythonServer/index.ts +9 -5
  39. package/src/pythonServer/kernelExecutor.ts +3 -156
  40. package/src/pythonServer/shiny/deps.ts +6 -0
  41. package/src/pythonServer/shiny/shinyServer.ts +63 -0
  42. package/src/pythonServer/starlette/starletteServer.ts +59 -0
  43. package/src/pythonServer/streamlit/deps.ts +12 -0
  44. package/src/pythonServer/streamlit/streamlitServer.ts +19 -20
  45. package/src/pythonServer/tornado/tornadoServer.ts +11 -55
  46. package/src/pythonWidget/pythonWidgetModel.ts +5 -2
  47. package/src/swConnection/mainConnectionManager.ts +28 -20
  48. package/src/tools.ts +9 -0
  49. package/src/type.ts +34 -12
  50. package/src/websocket/websocket.ts +9 -1
@@ -0,0 +1,285 @@
1
+ import { PageConfig, URLExt } from '@jupyterlab/coreutils';
2
+
3
+ import {
4
+ arrayBufferToBase64,
5
+ base64ToArrayBuffer,
6
+ base64ToString,
7
+ isBinaryContentType,
8
+ stringOrNone
9
+ } from '../tools';
10
+ import {
11
+ IBasePythonServer,
12
+ IDependencies,
13
+ IDict,
14
+ IKernelExecutor,
15
+ IPythonServerInitOptions,
16
+ JupyterPackFramework
17
+ } from '../type';
18
+ import websocketPatch from '../websocket/websocket.js?raw';
19
+ import { KernelExecutor } from './kernelExecutor';
20
+
21
+ export abstract class BasePythonServer implements IBasePythonServer {
22
+ constructor(options: KernelExecutor.IOptions) {
23
+ this._kernelExecutor = new KernelExecutor(options);
24
+ this._wsPatch = websocketPatch.replaceAll('"use strict";', '');
25
+ }
26
+
27
+ abstract reloadPythonServer(options: {
28
+ entryPath?: string;
29
+ initCode?: string;
30
+ }): Promise<void>;
31
+
32
+ get isDisposed(): boolean {
33
+ return this._isDisposed;
34
+ }
35
+
36
+ get kernelExecutor() {
37
+ return this._kernelExecutor;
38
+ }
39
+
40
+ dispose(): void {
41
+ if (this._isDisposed) {
42
+ return;
43
+ }
44
+ this._isDisposed = true;
45
+ this.disposePythonServer()
46
+ .then(() => {
47
+ this._kernelExecutor.dispose();
48
+ })
49
+ .catch(console.error);
50
+ }
51
+ async init(options: IPythonServerInitOptions): Promise<void> {
52
+ const patchCode = `
53
+ from jupyterpack.common import patch_all
54
+ patch_all()
55
+ `;
56
+ await this._kernelExecutor.executeCode({ code: patchCode });
57
+ if (!options.disableDependencies) {
58
+ const { dependencies } = options;
59
+ if (dependencies?.mamba) {
60
+ const mambaDeps = `
61
+ %mamba install ${dependencies.mamba.join(' ')}
62
+ True
63
+ `;
64
+ await this.kernelExecutor.executeCode(
65
+ {
66
+ code: mambaDeps
67
+ },
68
+ true
69
+ );
70
+ }
71
+ if (dependencies?.pip) {
72
+ const pipDeps = `
73
+ %pip install ${dependencies.pip.join(' ')}
74
+ True
75
+ `;
76
+ await this.kernelExecutor.executeCode(
77
+ {
78
+ code: pipDeps
79
+ },
80
+ true
81
+ );
82
+ }
83
+ }
84
+ }
85
+
86
+ async disposePythonServer(): Promise<void> {
87
+ await this.kernelExecutor.executeCode({
88
+ code: `${this._server_var}.dispose()`
89
+ });
90
+ for (const element of this._openedWebsockets) {
91
+ await this.closeWebsocket(element);
92
+ }
93
+ }
94
+
95
+ getResponseFunctionFactory(options: {
96
+ urlPath: string;
97
+ method: string;
98
+ headers: IDict;
99
+ params?: string;
100
+ content?: string;
101
+ }) {
102
+ const { method, urlPath, headers, params, content } = options;
103
+ const code = `await ${this._server_var}.get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
104
+ return code;
105
+ }
106
+
107
+ openWebsocketFunctionFactory(options: {
108
+ instanceId: string;
109
+ kernelId: string;
110
+ wsUrl: string;
111
+ protocol?: string;
112
+ }): string {
113
+ const { instanceId, kernelId, wsUrl, protocol } = options;
114
+ const code = `await ${this._server_var}.open_ws("${instanceId}", "${kernelId}", "${wsUrl}", ${stringOrNone(protocol)})`;
115
+ return code;
116
+ }
117
+
118
+ closeWebsocketFunctionFactory(options: {
119
+ instanceId: string;
120
+ kernelId: string;
121
+ wsUrl: string;
122
+ }): string {
123
+ const { instanceId, kernelId, wsUrl } = options;
124
+ const code = `await ${this._server_var}.close_ws("${instanceId}", "${kernelId}", "${wsUrl}")`;
125
+ return code;
126
+ }
127
+
128
+ sendWebsocketMessageFunctionFactory(options: {
129
+ instanceId: string;
130
+ kernelId: string;
131
+ wsUrl: string;
132
+ message: string;
133
+ }): string {
134
+ const { instanceId, kernelId, wsUrl, message } = options;
135
+ const code = `await ${this._server_var}.receive_ws_message("${instanceId}", "${kernelId}", "${wsUrl}", '''${message}''')`;
136
+ return code;
137
+ }
138
+
139
+ async openWebsocket(options: {
140
+ instanceId: string;
141
+ kernelId: string;
142
+ wsUrl: string;
143
+ protocol?: string;
144
+ }): Promise<void> {
145
+ const code = this.openWebsocketFunctionFactory(options);
146
+ if (code) {
147
+ try {
148
+ await this._kernelExecutor.executeCode({ code });
149
+ this._openedWebsockets.push(options);
150
+ } catch (e) {
151
+ console.error('Failed to open websocket', e);
152
+ }
153
+ } else {
154
+ throw new Error('Missing websocket open code');
155
+ }
156
+ }
157
+
158
+ async closeWebsocket(options: {
159
+ instanceId: string;
160
+ kernelId: string;
161
+ wsUrl: string;
162
+ }): Promise<void> {
163
+ const code = this.closeWebsocketFunctionFactory(options);
164
+ if (code) {
165
+ await this._kernelExecutor.executeCode({ code });
166
+ } else {
167
+ throw new Error('Missing websocket close code');
168
+ }
169
+ }
170
+
171
+ async sendWebsocketMessage(options: {
172
+ instanceId: string;
173
+ kernelId: string;
174
+ wsUrl: string;
175
+ message: string;
176
+ }): Promise<void> {
177
+ const code = this.sendWebsocketMessageFunctionFactory(options);
178
+ if (code) {
179
+ await this._kernelExecutor.executeCode({ code });
180
+ } else {
181
+ throw new Error('Missing websocket send code');
182
+ }
183
+ }
184
+
185
+ async getResponse(options: {
186
+ method: string;
187
+ urlPath: string;
188
+ headers: IDict;
189
+ requestBody?: ArrayBuffer;
190
+ params?: string;
191
+ }): Promise<IDict> {
192
+ const { method, urlPath, requestBody, params, headers } = options;
193
+ const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
194
+ const code = this.getResponseFunctionFactory({
195
+ method,
196
+ urlPath,
197
+ headers,
198
+ params,
199
+ content
200
+ });
201
+ const raw = await this._kernelExecutor.executeCode({ code }, true);
202
+ if (!raw) {
203
+ throw new Error(`Missing response for ${urlPath}`);
204
+ }
205
+ const jsonStr = raw.replaceAll("'", '');
206
+ const obj: {
207
+ headers: string;
208
+ status_code: number;
209
+ content: string;
210
+ } = JSON.parse(jsonStr);
211
+ const responseHeaders: IDict<string> = JSON.parse(atob(obj.headers));
212
+ const contentType: string | undefined =
213
+ responseHeaders?.['Content-Type'] ?? responseHeaders?.['content-type'];
214
+ let responseContent: string | Uint8Array;
215
+
216
+ if (isBinaryContentType(contentType)) {
217
+ responseContent = base64ToArrayBuffer(obj.content);
218
+ } else {
219
+ responseContent = base64ToString(obj.content);
220
+ }
221
+
222
+ if (contentType && contentType.toLowerCase().includes('text/html')) {
223
+ responseContent = (responseContent as string).replace(
224
+ '<head>',
225
+ `<head>\n<script>\n${this._wsPatch}\n</script>\n`
226
+ );
227
+ }
228
+
229
+ const decodedObj = {
230
+ status_code: obj.status_code,
231
+ headers: responseHeaders,
232
+ content: responseContent
233
+ };
234
+
235
+ return decodedObj;
236
+ }
237
+
238
+ protected buildBaseURL(options: {
239
+ instanceId: string;
240
+ kernelClientId: string;
241
+ framework: JupyterPackFramework;
242
+ }) {
243
+ const { instanceId, kernelClientId, framework } = options;
244
+ const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
245
+
246
+ const baseURL = URLExt.join(
247
+ fullLabextensionsUrl,
248
+ 'jupyterpack/static',
249
+ instanceId,
250
+ framework,
251
+ kernelClientId,
252
+ '/'
253
+ );
254
+ this._baseUrl = baseURL;
255
+
256
+ return baseURL;
257
+ }
258
+ protected mergeDependencies(
259
+ spkDeps?: IDependencies,
260
+ defaultDeps?: IDependencies
261
+ ): IDependencies | undefined {
262
+ if (!spkDeps) {
263
+ return defaultDeps;
264
+ }
265
+ if (!defaultDeps) {
266
+ return spkDeps;
267
+ }
268
+ const merged: IDependencies = {};
269
+ merged.mamba = [...(spkDeps.mamba ?? []), ...(defaultDeps.mamba ?? [])];
270
+ merged.pip = [...(spkDeps.pip ?? []), ...(defaultDeps.pip ?? [])];
271
+ return merged;
272
+ }
273
+
274
+ protected _baseUrl: string | undefined;
275
+ protected readonly _openedWebsockets: {
276
+ instanceId: string;
277
+ kernelId: string;
278
+ wsUrl: string;
279
+ }[] = [];
280
+ protected readonly _server_var = '__jupyterpack_python_server';
281
+
282
+ private _kernelExecutor: IKernelExecutor;
283
+ private _isDisposed: boolean = false;
284
+ private _wsPatch: string;
285
+ }
@@ -1,14 +1,15 @@
1
- import { stringOrNone } from '../../tools';
2
- import { IDict, JupyterPackFramework } from '../../type';
3
- import { KernelExecutor } from '../kernelExecutor';
1
+ import { IPythonServerInitOptions, JupyterPackFramework } from '../../type';
2
+ import { BasePythonServer } from '../baseServer';
3
+ import { DEPENDENCIES } from './deps';
4
+
5
+ export class DashServer extends BasePythonServer {
6
+ async init(options: IPythonServerInitOptions) {
7
+ const mergedOptions: IPythonServerInitOptions = {
8
+ ...options,
9
+ dependencies: this.mergeDependencies(options.dependencies, DEPENDENCIES)
10
+ };
11
+ await super.init(mergedOptions);
4
12
 
5
- export class DashServer extends KernelExecutor {
6
- async init(options: {
7
- initCode?: string;
8
- instanceId: string;
9
- kernelClientId: string;
10
- }) {
11
- await super.init(options);
12
13
  const { initCode, instanceId, kernelClientId } = options;
13
14
 
14
15
  const baseURL = this.buildBaseURL({
@@ -16,36 +17,20 @@ export class DashServer extends KernelExecutor {
16
17
  kernelClientId,
17
18
  framework: JupyterPackFramework.DASH
18
19
  });
19
- await this.executeCode({
20
+ await this.kernelExecutor.executeCode({
20
21
  code: `
21
22
  import os
22
23
  os.environ["DASH_URL_BASE_PATHNAME"] = "${baseURL}"
23
24
  `
24
25
  });
25
26
  if (initCode) {
26
- await this.executeCode({ code: initCode });
27
+ await this.kernelExecutor.executeCode({ code: initCode });
27
28
  }
28
29
  const loaderCode = `
29
30
  from jupyterpack.dash import DashServer
30
- ${this._DASH_SERVER_VAR} = DashServer(app, "${baseURL}")
31
+ ${this._server_var} = DashServer(app, "${baseURL}")
31
32
  `;
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()` });
33
+ await this.kernelExecutor.executeCode({ code: loaderCode });
49
34
  }
50
35
 
51
36
  async reloadPythonServer(options: {
@@ -54,13 +39,11 @@ export class DashServer extends KernelExecutor {
54
39
  }): Promise<void> {
55
40
  const { initCode } = options;
56
41
  if (initCode) {
57
- await this.executeCode({ code: initCode });
42
+ await this.kernelExecutor.executeCode({ code: initCode });
58
43
  }
59
- await this.executeCode(
60
- { code: `${this._DASH_SERVER_VAR}.reload(app)` },
44
+ await this.kernelExecutor.executeCode(
45
+ { code: `${this._server_var}.reload(app)` },
61
46
  true
62
47
  );
63
48
  }
64
-
65
- private _DASH_SERVER_VAR = '__jupyterpack_dash_server';
66
49
  }
@@ -0,0 +1,6 @@
1
+ import { IDependencies } from '../../type';
2
+
3
+ export const DEPENDENCIES: IDependencies = {
4
+ mamba: ['dash', 'werkzeug>=2.2,<3.0'],
5
+ pip: []
6
+ };
@@ -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,6 @@
1
+ import { IDependencies } from '../../type';
2
+
3
+ export const DEPENDENCIES: IDependencies = {
4
+ mamba: [],
5
+ pip: ['shiny', 'shinychat']
6
+ };
@@ -0,0 +1,63 @@
1
+ import { IPythonServerInitOptions, JupyterPackFramework } from '../../type';
2
+ import { BasePythonServer } from '../baseServer';
3
+ import { DEPENDENCIES } from './deps';
4
+
5
+ export class ShinyServer extends BasePythonServer {
6
+ async init(options: IPythonServerInitOptions) {
7
+ const mergedOptions: IPythonServerInitOptions = {
8
+ ...options,
9
+ dependencies: this.mergeDependencies(options.dependencies, DEPENDENCIES)
10
+ };
11
+ await super.init(mergedOptions);
12
+
13
+ const { instanceId, kernelClientId, entryPath } = options;
14
+ const baseURL = this.buildBaseURL({
15
+ instanceId,
16
+ kernelClientId,
17
+ framework: JupyterPackFramework.SHINY
18
+ });
19
+ const bootstrapCode = `
20
+ from jupyterpack.common import set_base_url_env
21
+ set_base_url_env("${baseURL}")
22
+ from jupyterpack.shiny import patch_shiny
23
+ patch_shiny()
24
+ `;
25
+ await this.kernelExecutor.executeCode({ code: bootstrapCode });
26
+ if (entryPath) {
27
+ const loaderCode = `
28
+ from jupyterpack.shiny import ShinyServer, get_shiny_app
29
+
30
+
31
+ ${this._server_var} = ShinyServer(get_shiny_app("${entryPath}"), "${baseURL}")
32
+ `;
33
+
34
+ await this.kernelExecutor.executeCode({ code: loaderCode });
35
+ }
36
+ }
37
+
38
+ async disposePythonServer(): Promise<void> {
39
+ await this.kernelExecutor.executeCode({
40
+ code: `${this._server_var}.dispose()`
41
+ });
42
+ for (const element of this._openedWebsockets) {
43
+ await this.closeWebsocket(element);
44
+ }
45
+ }
46
+
47
+ async reloadPythonServer(options: {
48
+ entryPath?: string;
49
+ initCode?: string;
50
+ }): Promise<void> {
51
+ const { entryPath } = options;
52
+ if (entryPath) {
53
+ const reloadCode = `
54
+ from jupyterpack.shiny import get_shiny_app
55
+
56
+ await ${this._server_var}.dispose()
57
+ ${this._server_var}.reload(get_shiny_app("${entryPath}"))
58
+ `;
59
+
60
+ await this.kernelExecutor.executeCode({ code: reloadCode }, true);
61
+ }
62
+ }
63
+ }