jupyterpack 0.2.1 → 0.4.0

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 (83) hide show
  1. package/README.md +2 -0
  2. package/lib/document/commands.d.ts +8 -0
  3. package/lib/document/commands.js +76 -0
  4. package/lib/document/iframePanel.d.ts +4 -1
  5. package/lib/document/plugin.d.ts +2 -1
  6. package/lib/document/plugin.js +22 -4
  7. package/lib/document/toolbar.d.ts +9 -0
  8. package/lib/document/toolbar.js +20 -0
  9. package/lib/document/widgetFactory.d.ts +2 -1
  10. package/lib/document/widgetFactory.js +15 -6
  11. package/lib/index.d.ts +1 -1
  12. package/lib/pythonServer/dash/dashServer.d.ts +22 -0
  13. package/lib/pythonServer/dash/dashServer.js +47 -0
  14. package/lib/pythonServer/index.d.ts +5 -0
  15. package/lib/pythonServer/index.js +9 -0
  16. package/lib/pythonServer/kernelExecutor.d.ts +71 -0
  17. package/lib/pythonServer/kernelExecutor.js +140 -0
  18. package/lib/pythonServer/streamlit/streamlitServer.d.ts +14 -0
  19. package/lib/pythonServer/streamlit/streamlitServer.js +51 -0
  20. package/lib/pythonServer/tornado/tornadoServer.d.ts +34 -0
  21. package/lib/pythonServer/tornado/tornadoServer.js +68 -0
  22. package/lib/pythonWidget/comm.d.ts +11 -0
  23. package/lib/pythonWidget/comm.js +52 -0
  24. package/lib/pythonWidget/pythonWidget.d.ts +6 -0
  25. package/lib/pythonWidget/pythonWidget.js +28 -3
  26. package/lib/pythonWidget/pythonWidgetModel.d.ts +27 -6
  27. package/lib/pythonWidget/pythonWidgetModel.js +101 -15
  28. package/lib/sandpackWidget/sandpackFilesModel.d.ts +6 -2
  29. package/lib/sandpackWidget/sandpackFilesModel.js +13 -2
  30. package/lib/sandpackWidget/sandpackPanel.d.ts +10 -1
  31. package/lib/sandpackWidget/sandpackPanel.js +38 -3
  32. package/lib/swConnection/index.js +2 -2
  33. package/lib/{pythonWidget/connectionManager.d.ts → swConnection/mainConnectionManager.d.ts} +10 -0
  34. package/lib/swConnection/mainConnectionManager.js +93 -0
  35. package/lib/swConnection/sw.js +1 -1
  36. package/lib/swConnection/swCommManager.d.ts +11 -0
  37. package/lib/swConnection/{comm_manager.js → swCommManager.js} +5 -0
  38. package/lib/token.d.ts +2 -1
  39. package/lib/token.js +1 -0
  40. package/lib/tools.d.ts +9 -0
  41. package/lib/tools.js +75 -0
  42. package/lib/type.d.ts +64 -3
  43. package/lib/type.js +2 -0
  44. package/lib/websocket/websocket.d.ts +0 -0
  45. package/lib/websocket/websocket.js +152 -0
  46. package/package.json +8 -5
  47. package/src/document/commands.ts +91 -0
  48. package/src/document/iframePanel.ts +4 -1
  49. package/src/document/plugin.ts +28 -7
  50. package/src/document/toolbar.ts +39 -0
  51. package/src/document/widgetFactory.ts +17 -6
  52. package/src/global.d.ts +9 -0
  53. package/src/pythonServer/dash/dashServer.ts +66 -0
  54. package/src/pythonServer/index.ts +18 -0
  55. package/src/pythonServer/kernelExecutor.ts +243 -0
  56. package/src/pythonServer/streamlit/streamlitServer.ts +67 -0
  57. package/src/pythonServer/tornado/tornadoServer.ts +97 -0
  58. package/src/pythonWidget/comm.ts +65 -0
  59. package/src/pythonWidget/pythonWidget.ts +38 -3
  60. package/src/pythonWidget/pythonWidgetModel.ts +155 -34
  61. package/src/sandpackWidget/sandpackFilesModel.ts +17 -3
  62. package/src/sandpackWidget/sandpackPanel.ts +45 -4
  63. package/src/swConnection/index.ts +5 -2
  64. package/src/swConnection/mainConnectionManager.ts +121 -0
  65. package/src/swConnection/sw.ts +1 -1
  66. package/src/swConnection/{comm_manager.ts → swCommManager.ts} +6 -0
  67. package/src/token.ts +5 -1
  68. package/src/tools.ts +91 -0
  69. package/src/type.ts +76 -4
  70. package/src/websocket/websocket.ts +216 -0
  71. package/style/base.css +7 -0
  72. package/style/icons/autoreload.svg +16 -0
  73. package/style/icons/box.svg +12 -0
  74. package/style/icons/externallink.svg +10 -0
  75. package/lib/pythonWidget/connectionManager.js +0 -27
  76. package/lib/pythonWidget/kernelExecutor.d.ts +0 -27
  77. package/lib/pythonWidget/kernelExecutor.js +0 -104
  78. package/lib/swConnection/comm_manager.d.ts +0 -6
  79. package/lib/swConnection/connection_manager.d.ts +0 -18
  80. package/lib/swConnection/connection_manager.js +0 -27
  81. package/src/pythonWidget/connectionManager.ts +0 -43
  82. package/src/pythonWidget/kernelExecutor.ts +0 -140
  83. package/src/swConnection/connection_manager.ts +0 -43
package/README.md CHANGED
@@ -10,6 +10,8 @@
10
10
  - **Python Web Apps**: Serve Python web applications directly in the browser using JupyterLite's in-browser Python kernel. `jupyterpack` currently supports Dash.
11
11
  - **JavaScript Web Apps**: Bundle and serve JavaScript web applications using in-browser bundlers.
12
12
 
13
+ ![Image](https://github.com/user-attachments/assets/22849fe8-199f-4d9f-ad45-055bccf88bad)
14
+
13
15
  ## Installation
14
16
 
15
17
  You can install `jupyterpack` using `pip` or `conda`
@@ -0,0 +1,8 @@
1
+ import { CommandRegistry } from '@lumino/commands';
2
+ import { IJupyterpackDocTracker } from '../type';
3
+ export declare const CommandIDs: {
4
+ RELOAD: string;
5
+ TOGGLE_AUTORELOAD: string;
6
+ OPEN_SPECTA: string;
7
+ };
8
+ export declare function addCommands(commands: CommandRegistry, tracker: IJupyterpackDocTracker): void;
@@ -0,0 +1,76 @@
1
+ import { refreshIcon } from '@jupyterlab/ui-components';
2
+ import { autoReloadIcon, linkIcon } from '../tools';
3
+ import { PageConfig, URLExt } from '@jupyterlab/coreutils';
4
+ export const CommandIDs = {
5
+ RELOAD: 'jupyterpack:reload',
6
+ TOGGLE_AUTORELOAD: 'jupyterpack:toggleAutoreload',
7
+ OPEN_SPECTA: 'jupyterpack:openInSpecta'
8
+ };
9
+ const labBaseUrl = PageConfig.getOption('baseUrl');
10
+ function getCurrentIframPanel(tracker) {
11
+ var _a;
12
+ const current = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.content;
13
+ if (!current) {
14
+ return;
15
+ }
16
+ const widget = current.widgets[0];
17
+ if (!widget) {
18
+ return;
19
+ }
20
+ return widget;
21
+ }
22
+ export function addCommands(commands, tracker) {
23
+ commands.addCommand(CommandIDs.RELOAD, {
24
+ caption: 'Reload',
25
+ isEnabled: () => {
26
+ return tracker.currentWidget !== null;
27
+ },
28
+ icon: refreshIcon,
29
+ execute: async () => {
30
+ const widget = getCurrentIframPanel(tracker);
31
+ if (widget) {
32
+ await widget.reload();
33
+ }
34
+ }
35
+ });
36
+ const commandState = { toggled: false };
37
+ commands.addCommand(CommandIDs.TOGGLE_AUTORELOAD, {
38
+ isEnabled: () => {
39
+ return tracker.currentWidget !== null;
40
+ },
41
+ isToggled: () => {
42
+ const widget = getCurrentIframPanel(tracker);
43
+ return Boolean(widget === null || widget === void 0 ? void 0 : widget.autoreload);
44
+ },
45
+ icon: autoReloadIcon,
46
+ caption: e => {
47
+ return commandState.toggled
48
+ ? 'Auto-reload enabled'
49
+ : 'Auto-reload disabled';
50
+ },
51
+ execute: async () => {
52
+ const widget = getCurrentIframPanel(tracker);
53
+ if (widget) {
54
+ widget.autoreload = !(widget === null || widget === void 0 ? void 0 : widget.autoreload);
55
+ commands.notifyCommandChanged(CommandIDs.TOGGLE_AUTORELOAD);
56
+ }
57
+ }
58
+ });
59
+ commands.addCommand(CommandIDs.OPEN_SPECTA, {
60
+ caption: 'Open in Specta',
61
+ isEnabled: () => {
62
+ return tracker.currentWidget !== null;
63
+ },
64
+ icon: linkIcon,
65
+ execute: async () => {
66
+ var _a;
67
+ const context = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context;
68
+ if (!context) {
69
+ return;
70
+ }
71
+ const spectaUrl = new URL(URLExt.join(labBaseUrl, 'specta'), window.location.origin);
72
+ spectaUrl.searchParams.set('path', context.path);
73
+ window.open(spectaUrl.toString(), '_blank');
74
+ }
75
+ });
76
+ }
@@ -1,7 +1,10 @@
1
1
  import { Widget } from '@lumino/widgets';
2
- export declare class IFramePanel extends Widget {
2
+ export declare abstract class IFramePanel extends Widget {
3
3
  constructor();
4
4
  toggleSpinner(show: boolean): void;
5
+ abstract reload(): Promise<void>;
6
+ abstract autoreload: boolean;
7
+ abstract isReady: Promise<void>;
5
8
  protected _iframe: HTMLIFrameElement;
6
9
  protected _spinner: HTMLDivElement;
7
10
  }
@@ -1,2 +1,3 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
- export declare const spkPlugin: JupyterFrontEndPlugin<void>;
2
+ import { IJupyterpackDocTracker } from '../type';
3
+ export declare const spkPlugin: JupyterFrontEndPlugin<IJupyterpackDocTracker>;
@@ -1,12 +1,20 @@
1
1
  import { JupyterPackWidgetFactory } from './widgetFactory';
2
- import { IConnectionManagerToken } from '../token';
2
+ import { IConnectionManagerToken, IJupyterpackDocTrackerToken } from '../token';
3
+ import { WidgetTracker } from '@jupyterlab/apputils';
4
+ import { logoIcon } from '../tools';
5
+ import { addCommands } from './commands';
3
6
  const FACTORY = 'jupyterpack';
4
7
  const CONTENT_TYPE = 'jupyterpack';
5
8
  export const spkPlugin = {
6
9
  id: 'jupyterpack:spkplugin',
7
10
  requires: [IConnectionManagerToken],
8
11
  autoStart: true,
12
+ provides: IJupyterpackDocTrackerToken,
9
13
  activate: (app, connectionManager) => {
14
+ const tracker = new WidgetTracker({
15
+ namespace: FACTORY
16
+ });
17
+ addCommands(app.commands, tracker);
10
18
  const widgetFactory = new JupyterPackWidgetFactory({
11
19
  name: FACTORY,
12
20
  modelName: 'text',
@@ -14,7 +22,8 @@ export const spkPlugin = {
14
22
  defaultFor: [CONTENT_TYPE],
15
23
  commands: app.commands,
16
24
  manager: app.serviceManager,
17
- connectionManager
25
+ connectionManager,
26
+ tracker
18
27
  });
19
28
  // Registering the widget factory
20
29
  app.docRegistry.addWidgetFactory(widgetFactory);
@@ -22,10 +31,19 @@ export const spkPlugin = {
22
31
  app.docRegistry.addFileType({
23
32
  name: CONTENT_TYPE,
24
33
  displayName: 'SPK',
25
- mimeTypes: ['text/json'],
34
+ mimeTypes: ['application/json'],
26
35
  extensions: ['.spk', '.SPK'],
27
36
  fileFormat: 'json',
28
- contentType: CONTENT_TYPE
37
+ contentType: CONTENT_TYPE,
38
+ icon: logoIcon
39
+ });
40
+ widgetFactory.widgetCreated.connect((_, widget) => {
41
+ widget.title.icon = logoIcon;
42
+ widget.context.pathChanged.connect(() => {
43
+ tracker.save(widget);
44
+ });
45
+ tracker.add(widget);
29
46
  });
47
+ return tracker;
30
48
  }
31
49
  };
@@ -0,0 +1,9 @@
1
+ import { ReactiveToolbar } from '@jupyterlab/ui-components';
2
+ import { CommandRegistry } from '@lumino/commands';
3
+ import { IJupyterpackDocTracker } from '../type';
4
+ export declare class ToolbarWidget extends ReactiveToolbar {
5
+ constructor(options: {
6
+ tracker: IJupyterpackDocTracker;
7
+ commands: CommandRegistry;
8
+ });
9
+ }
@@ -0,0 +1,20 @@
1
+ import { CommandToolbarButton, ReactiveToolbar } from '@jupyterlab/ui-components';
2
+ import { CommandIDs } from './commands';
3
+ export class ToolbarWidget extends ReactiveToolbar {
4
+ constructor(options) {
5
+ super();
6
+ this.addClass('jupyterpack-toolbar');
7
+ this.addItem('Reload', new CommandToolbarButton({
8
+ commands: options.commands,
9
+ id: CommandIDs.RELOAD
10
+ }));
11
+ this.addItem('Toggle Auto Reload', new CommandToolbarButton({
12
+ id: CommandIDs.TOGGLE_AUTORELOAD,
13
+ commands: options.commands
14
+ }));
15
+ this.addItem('Open Specta', new CommandToolbarButton({
16
+ id: CommandIDs.OPEN_SPECTA,
17
+ commands: options.commands
18
+ }));
19
+ }
20
+ }
@@ -1,12 +1,13 @@
1
1
  import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
2
2
  import { ServiceManager } from '@jupyterlab/services';
3
3
  import { CommandRegistry } from '@lumino/commands';
4
- import { IConnectionManager } from '../type';
4
+ import { IConnectionManager, IJupyterpackDocTracker } from '../type';
5
5
  import { JupyterPackDocWidget } from './jupyterpackDocWidget';
6
6
  interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
7
7
  commands: CommandRegistry;
8
8
  manager: ServiceManager.IManager;
9
9
  connectionManager: IConnectionManager;
10
+ tracker: IJupyterpackDocTracker;
10
11
  }
11
12
  export declare class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWidget> {
12
13
  private options;
@@ -1,11 +1,12 @@
1
1
  import { ABCWidgetFactory } from '@jupyterlab/docregistry';
2
+ import { UUID } from '@lumino/coreutils';
2
3
  import { Panel } from '@lumino/widgets';
3
- import { JupyterPackFramework } from '../type';
4
+ import { PythonWidget } from '../pythonWidget/pythonWidget';
5
+ import { PythonWidgetModel } from '../pythonWidget/pythonWidgetModel';
4
6
  import { SandpackPanel } from '../sandpackWidget/sandpackPanel';
7
+ import { JupyterPackFramework } from '../type';
5
8
  import { JupyterPackDocWidget } from './jupyterpackDocWidget';
6
- import { PythonWidgetModel } from '../pythonWidget/pythonWidgetModel';
7
- import { UUID } from '@lumino/coreutils';
8
- import { PythonWidget } from '../pythonWidget/pythonWidget';
9
+ import { ToolbarWidget } from './toolbar';
9
10
  export class JupyterPackWidgetFactory extends ABCWidgetFactory {
10
11
  constructor(options) {
11
12
  super(options);
@@ -32,8 +33,11 @@ export class JupyterPackWidgetFactory extends ABCWidgetFactory {
32
33
  content.addWidget(jpContent);
33
34
  break;
34
35
  }
35
- case JupyterPackFramework.DASH: {
36
+ case JupyterPackFramework.DASH:
37
+ case JupyterPackFramework.STREAMLIT:
38
+ case JupyterPackFramework.TORNADO: {
36
39
  const model = new PythonWidgetModel({
40
+ jpackModel,
37
41
  context,
38
42
  manager: this.options.manager,
39
43
  contentsManager: this._contentsManager,
@@ -52,9 +56,14 @@ export class JupyterPackWidgetFactory extends ABCWidgetFactory {
52
56
  }
53
57
  }
54
58
  });
59
+ const toolbar = new ToolbarWidget({
60
+ tracker: this.options.tracker,
61
+ commands: this.options.commands
62
+ });
55
63
  return new JupyterPackDocWidget({
56
64
  context,
57
- content
65
+ content,
66
+ toolbar
58
67
  });
59
68
  }
60
69
  }
package/lib/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: (import("@jupyterlab/application").JupyterFrontEndPlugin<void> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IConnectionManager>)[];
1
+ declare const _default: (import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IJupyterpackDocTracker> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IConnectionManager>)[];
2
2
  export default _default;
@@ -0,0 +1,22 @@
1
+ import { IDict } from '../../type';
2
+ import { KernelExecutor } from '../kernelExecutor';
3
+ export declare class DashServer extends KernelExecutor {
4
+ init(options: {
5
+ initCode?: string;
6
+ instanceId: string;
7
+ kernelClientId: string;
8
+ }): Promise<void>;
9
+ getResponseFunctionFactory(options: {
10
+ urlPath: string;
11
+ method: string;
12
+ headers: IDict;
13
+ params?: string;
14
+ content?: string;
15
+ }): string;
16
+ disposePythonServer(): Promise<void>;
17
+ reloadPythonServer(options: {
18
+ entryPath?: string;
19
+ initCode?: string;
20
+ }): Promise<void>;
21
+ private _DASH_SERVER_VAR;
22
+ }
@@ -0,0 +1,47 @@
1
+ import { stringOrNone } from '../../tools';
2
+ import { JupyterPackFramework } from '../../type';
3
+ import { KernelExecutor } from '../kernelExecutor';
4
+ export class DashServer extends KernelExecutor {
5
+ constructor() {
6
+ super(...arguments);
7
+ this._DASH_SERVER_VAR = '__jupyterpack_dash_server';
8
+ }
9
+ async init(options) {
10
+ await super.init(options);
11
+ const { initCode, instanceId, kernelClientId } = options;
12
+ const baseURL = this.buildBaseURL({
13
+ instanceId,
14
+ kernelClientId,
15
+ framework: JupyterPackFramework.DASH
16
+ });
17
+ await this.executeCode({
18
+ code: `
19
+ import os
20
+ os.environ["DASH_URL_BASE_PATHNAME"] = "${baseURL}"
21
+ `
22
+ });
23
+ if (initCode) {
24
+ await this.executeCode({ code: initCode });
25
+ }
26
+ const loaderCode = `
27
+ from jupyterpack.dash import DashServer
28
+ ${this._DASH_SERVER_VAR} = DashServer(app, "${baseURL}")
29
+ `;
30
+ await this.executeCode({ code: loaderCode });
31
+ }
32
+ getResponseFunctionFactory(options) {
33
+ const { method, urlPath, headers, params, content } = options;
34
+ const code = `${this._DASH_SERVER_VAR}.get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
35
+ return code;
36
+ }
37
+ async disposePythonServer() {
38
+ await this.executeCode({ code: `${this._DASH_SERVER_VAR}.dispose()` });
39
+ }
40
+ async reloadPythonServer(options) {
41
+ const { initCode } = options;
42
+ if (initCode) {
43
+ await this.executeCode({ code: initCode });
44
+ }
45
+ await this.executeCode({ code: `${this._DASH_SERVER_VAR}.reload(app)` }, true);
46
+ }
47
+ }
@@ -0,0 +1,5 @@
1
+ import { IKernelExecutor, JupyterPackFramework } from '../type';
2
+ import { KernelExecutor } from './kernelExecutor';
3
+ type KernelExecutorConstructor = new (options: KernelExecutor.IOptions) => IKernelExecutor;
4
+ export declare const PYTHON_SERVER: Map<JupyterPackFramework, KernelExecutorConstructor>;
5
+ export {};
@@ -0,0 +1,9 @@
1
+ import { JupyterPackFramework } from '../type';
2
+ import { DashServer } from './dash/dashServer';
3
+ import { StreamlitServer } from './streamlit/streamlitServer';
4
+ import { TornadoServer } from './tornado/tornadoServer';
5
+ export const PYTHON_SERVER = new Map([
6
+ [JupyterPackFramework.DASH, DashServer],
7
+ [JupyterPackFramework.STREAMLIT, StreamlitServer],
8
+ [JupyterPackFramework.TORNADO, TornadoServer]
9
+ ]);
@@ -0,0 +1,71 @@
1
+ import { KernelMessage, Session } from '@jupyterlab/services';
2
+ import { IDict, IKernelExecutor, JupyterPackFramework } from '../type';
3
+ export declare abstract class KernelExecutor implements IKernelExecutor {
4
+ constructor(options: KernelExecutor.IOptions);
5
+ get isDisposed(): boolean;
6
+ abstract disposePythonServer(): Promise<void>;
7
+ abstract reloadPythonServer(options: {
8
+ entryPath?: string;
9
+ initCode?: string;
10
+ }): Promise<void>;
11
+ abstract getResponseFunctionFactory(options: {
12
+ urlPath: string;
13
+ method: string;
14
+ headers: IDict;
15
+ params?: string;
16
+ content?: string;
17
+ }): string;
18
+ init(options: {
19
+ entryPath?: string;
20
+ initCode?: string;
21
+ instanceId: string;
22
+ kernelClientId: string;
23
+ }): Promise<void>;
24
+ openWebsocketFunctionFactory(options: {
25
+ instanceId: string;
26
+ kernelId: string;
27
+ wsUrl: string;
28
+ protocol?: string;
29
+ }): string | undefined;
30
+ sendWebsocketMessageFunctionFactory(options: {
31
+ instanceId: string;
32
+ kernelId: string;
33
+ wsUrl: string;
34
+ message: string;
35
+ }): string | undefined;
36
+ openWebsocket(options: {
37
+ instanceId: string;
38
+ kernelId: string;
39
+ wsUrl: string;
40
+ protocol?: string;
41
+ }): Promise<void>;
42
+ sendWebsocketMessage(options: {
43
+ instanceId: string;
44
+ kernelId: string;
45
+ wsUrl: string;
46
+ message: string;
47
+ }): Promise<void>;
48
+ getResponse(options: {
49
+ method: string;
50
+ urlPath: string;
51
+ headers: IDict;
52
+ requestBody?: ArrayBuffer;
53
+ params?: string;
54
+ }): Promise<IDict>;
55
+ executeCode(code: KernelMessage.IExecuteRequestMsg['content'], waitForResult?: boolean): Promise<string | null>;
56
+ dispose(): void;
57
+ protected buildBaseURL(options: {
58
+ instanceId: string;
59
+ kernelClientId: string;
60
+ framework: JupyterPackFramework;
61
+ }): string;
62
+ protected _baseUrl: string | undefined;
63
+ private _isDisposed;
64
+ private _sessionConnection;
65
+ private _wsPatch;
66
+ }
67
+ export declare namespace KernelExecutor {
68
+ interface IOptions {
69
+ sessionConnection: Session.ISessionConnection;
70
+ }
71
+ }
@@ -0,0 +1,140 @@
1
+ import { PageConfig, URLExt } from '@jupyterlab/coreutils';
2
+ import stripAnsi from 'strip-ansi';
3
+ import { arrayBufferToBase64, base64ToArrayBuffer, base64ToString, isBinaryContentType } from '../tools';
4
+ import websocketPatch from '../websocket/websocket.js?raw';
5
+ export class KernelExecutor {
6
+ constructor(options) {
7
+ this._isDisposed = false;
8
+ this._sessionConnection = options.sessionConnection;
9
+ this._wsPatch = websocketPatch.replaceAll('"use strict";', '');
10
+ }
11
+ get isDisposed() {
12
+ return this._isDisposed;
13
+ }
14
+ async init(options) {
15
+ const patchCode = `
16
+ from jupyterpack.common import patch_all
17
+ patch_all()
18
+ `;
19
+ await this.executeCode({ code: patchCode });
20
+ }
21
+ openWebsocketFunctionFactory(options) {
22
+ return undefined;
23
+ }
24
+ sendWebsocketMessageFunctionFactory(options) {
25
+ return undefined;
26
+ }
27
+ async openWebsocket(options) {
28
+ const code = this.openWebsocketFunctionFactory(options);
29
+ if (code) {
30
+ await this.executeCode({ code });
31
+ }
32
+ }
33
+ async sendWebsocketMessage(options) {
34
+ const code = this.sendWebsocketMessageFunctionFactory(options);
35
+ if (code) {
36
+ await this.executeCode({ code });
37
+ }
38
+ }
39
+ async getResponse(options) {
40
+ var _a;
41
+ const { method, urlPath, requestBody, params, headers } = options;
42
+ const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
43
+ const code = this.getResponseFunctionFactory({
44
+ method,
45
+ urlPath,
46
+ headers,
47
+ params,
48
+ content
49
+ });
50
+ const raw = await this.executeCode({ code }, true);
51
+ if (!raw) {
52
+ throw new Error(`Missing response for ${urlPath}`);
53
+ }
54
+ const jsonStr = raw.replaceAll("'", '');
55
+ const obj = JSON.parse(jsonStr);
56
+ const responseHeaders = JSON.parse(atob(obj.headers));
57
+ const contentType = (_a = responseHeaders === null || responseHeaders === void 0 ? void 0 : responseHeaders['Content-Type']) !== null && _a !== void 0 ? _a : responseHeaders === null || responseHeaders === void 0 ? void 0 : responseHeaders['content-type'];
58
+ let responseContent;
59
+ if (isBinaryContentType(contentType)) {
60
+ responseContent = base64ToArrayBuffer(obj.content);
61
+ }
62
+ else {
63
+ responseContent = base64ToString(obj.content);
64
+ }
65
+ if (contentType && contentType.toLowerCase() === 'text/html') {
66
+ responseContent = responseContent.replace('<head>', `<head>\n<script>\n${this._wsPatch}\n</script>\n`);
67
+ }
68
+ const decodedObj = {
69
+ status_code: obj.status_code,
70
+ headers: responseHeaders,
71
+ content: responseContent
72
+ };
73
+ return decodedObj;
74
+ }
75
+ async executeCode(code, waitForResult) {
76
+ var _a;
77
+ const kernel = (_a = this._sessionConnection) === null || _a === void 0 ? void 0 : _a.kernel;
78
+ if (!kernel) {
79
+ throw new Error('Session has no kernel.');
80
+ }
81
+ return new Promise((resolve, reject) => {
82
+ const future = kernel.requestExecute(code, false, undefined);
83
+ let executeResult = '';
84
+ future.onIOPub = (msg) => {
85
+ const msgType = msg.header.msg_type;
86
+ switch (msgType) {
87
+ case 'execute_result': {
88
+ if (waitForResult) {
89
+ const content = msg.content
90
+ .data['text/plain'];
91
+ executeResult += content;
92
+ resolve(executeResult);
93
+ }
94
+ break;
95
+ }
96
+ case 'stream': {
97
+ const content = msg.content;
98
+ if (content.text.length === 0) {
99
+ break;
100
+ }
101
+ if (content.name === 'stderr') {
102
+ console.error('Kernel stream:', content.text);
103
+ }
104
+ else {
105
+ console.log('Kernel stream:', content.text);
106
+ }
107
+ break;
108
+ }
109
+ case 'error': {
110
+ console.error('Kernel operation failed', code.code, msg.content.traceback
111
+ .map((it) => stripAnsi(it))
112
+ .join('\n'));
113
+ reject(msg.content);
114
+ break;
115
+ }
116
+ default:
117
+ break;
118
+ }
119
+ };
120
+ if (!waitForResult) {
121
+ resolve(null);
122
+ // future.dispose() # TODO
123
+ }
124
+ });
125
+ }
126
+ dispose() {
127
+ if (this._isDisposed) {
128
+ return;
129
+ }
130
+ this._isDisposed = true;
131
+ this._sessionConnection.dispose();
132
+ }
133
+ buildBaseURL(options) {
134
+ const { instanceId, kernelClientId, framework } = options;
135
+ const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
136
+ const baseURL = URLExt.join(fullLabextensionsUrl, 'jupyterpack/static', instanceId, framework, kernelClientId, '/');
137
+ this._baseUrl = baseURL;
138
+ return baseURL;
139
+ }
140
+ }
@@ -0,0 +1,14 @@
1
+ import { TornadoServer } from '../tornado/tornadoServer';
2
+ export declare class StreamlitServer extends TornadoServer {
3
+ init(options: {
4
+ entryPath?: string;
5
+ initCode?: string;
6
+ instanceId: string;
7
+ kernelClientId: string;
8
+ }): Promise<void>;
9
+ reloadPythonServer(options: {
10
+ entryPath?: string;
11
+ initCode?: string;
12
+ }): Promise<void>;
13
+ protected _SERVER_VAR: string;
14
+ }
@@ -0,0 +1,51 @@
1
+ import { JupyterPackFramework } from '../../type';
2
+ import { TornadoServer } from '../tornado/tornadoServer';
3
+ export class StreamlitServer extends TornadoServer {
4
+ constructor() {
5
+ super(...arguments);
6
+ this._SERVER_VAR = '__jupyterpack_streamlit_server';
7
+ }
8
+ async init(options) {
9
+ const { instanceId, kernelClientId, entryPath } = options;
10
+ if (!entryPath) {
11
+ throw new Error('Missing streamlit entry path, please check your SPK file');
12
+ }
13
+ const baseURL = this.buildBaseURL({
14
+ instanceId,
15
+ kernelClientId,
16
+ framework: JupyterPackFramework.STREAMLIT
17
+ });
18
+ const patchCode = `
19
+ from jupyterpack.common import set_base_url_env, patch_tornado, patch_all
20
+ patch_all()
21
+ patch_tornado()
22
+ set_base_url_env("${baseURL}")
23
+ `;
24
+ await this.executeCode({ code: patchCode });
25
+ const bootstrapCode = `
26
+ from jupyterpack.streamlit import patch_streamlit
27
+ patch_streamlit()
28
+ `;
29
+ await this.executeCode({ code: bootstrapCode });
30
+ const stCode = `
31
+ from jupyterpack.streamlit import StreamlitServer, create_streamlit_app
32
+ __jupyterpack_st_server, __jupyterpack_tor_app = await create_streamlit_app("${entryPath}", "${baseURL}")
33
+ ${this._SERVER_VAR} = StreamlitServer(__jupyterpack_tor_app, "${baseURL}", __jupyterpack_st_server)
34
+ `;
35
+ await this.executeCode({ code: stCode });
36
+ }
37
+ async reloadPythonServer(options) {
38
+ const { entryPath } = options;
39
+ if (!entryPath || !this._baseUrl) {
40
+ return;
41
+ }
42
+ const reloadCode = `
43
+ ${this._SERVER_VAR}.dispose()
44
+ __jupyterpack_st_server, __jupyterpack_tor_app = await create_streamlit_app("${entryPath}", "${this._baseUrl}")
45
+ ${this._SERVER_VAR}.reload(__jupyterpack_tor_app, __jupyterpack_st_server)
46
+ `;
47
+ await this.executeCode({
48
+ code: reloadCode
49
+ }, true);
50
+ }
51
+ }
@@ -0,0 +1,34 @@
1
+ import { IDict } from '../../type';
2
+ import { KernelExecutor } from '../kernelExecutor';
3
+ export declare class TornadoServer extends KernelExecutor {
4
+ init(options: {
5
+ initCode?: string;
6
+ instanceId: string;
7
+ kernelClientId: string;
8
+ }): Promise<void>;
9
+ getResponseFunctionFactory(options: {
10
+ urlPath: string;
11
+ method: string;
12
+ headers: IDict;
13
+ params?: string;
14
+ content?: string;
15
+ }): string;
16
+ openWebsocketFunctionFactory(options: {
17
+ instanceId: string;
18
+ kernelId: string;
19
+ wsUrl: string;
20
+ protocol?: string;
21
+ }): string;
22
+ sendWebsocketMessageFunctionFactory(options: {
23
+ instanceId: string;
24
+ kernelId: string;
25
+ wsUrl: string;
26
+ message: string;
27
+ }): string;
28
+ disposePythonServer(): Promise<void>;
29
+ reloadPythonServer(options: {
30
+ entryPath?: string;
31
+ initCode?: string;
32
+ }): Promise<void>;
33
+ protected _SERVER_VAR: string;
34
+ }