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.
- package/README.md +2 -0
- package/lib/document/commands.d.ts +8 -0
- package/lib/document/commands.js +76 -0
- package/lib/document/iframePanel.d.ts +4 -1
- package/lib/document/plugin.d.ts +2 -1
- package/lib/document/plugin.js +22 -4
- package/lib/document/toolbar.d.ts +9 -0
- package/lib/document/toolbar.js +20 -0
- package/lib/document/widgetFactory.d.ts +2 -1
- package/lib/document/widgetFactory.js +15 -6
- package/lib/index.d.ts +1 -1
- package/lib/pythonServer/dash/dashServer.d.ts +22 -0
- package/lib/pythonServer/dash/dashServer.js +47 -0
- package/lib/pythonServer/index.d.ts +5 -0
- package/lib/pythonServer/index.js +9 -0
- package/lib/pythonServer/kernelExecutor.d.ts +71 -0
- package/lib/pythonServer/kernelExecutor.js +140 -0
- package/lib/pythonServer/streamlit/streamlitServer.d.ts +14 -0
- package/lib/pythonServer/streamlit/streamlitServer.js +51 -0
- package/lib/pythonServer/tornado/tornadoServer.d.ts +34 -0
- package/lib/pythonServer/tornado/tornadoServer.js +68 -0
- package/lib/pythonWidget/comm.d.ts +11 -0
- package/lib/pythonWidget/comm.js +52 -0
- package/lib/pythonWidget/pythonWidget.d.ts +6 -0
- package/lib/pythonWidget/pythonWidget.js +28 -3
- package/lib/pythonWidget/pythonWidgetModel.d.ts +27 -6
- package/lib/pythonWidget/pythonWidgetModel.js +101 -15
- package/lib/sandpackWidget/sandpackFilesModel.d.ts +6 -2
- package/lib/sandpackWidget/sandpackFilesModel.js +13 -2
- package/lib/sandpackWidget/sandpackPanel.d.ts +10 -1
- package/lib/sandpackWidget/sandpackPanel.js +38 -3
- package/lib/swConnection/index.js +2 -2
- package/lib/{pythonWidget/connectionManager.d.ts → swConnection/mainConnectionManager.d.ts} +10 -0
- package/lib/swConnection/mainConnectionManager.js +93 -0
- package/lib/swConnection/sw.js +1 -1
- package/lib/swConnection/swCommManager.d.ts +11 -0
- package/lib/swConnection/{comm_manager.js → swCommManager.js} +5 -0
- package/lib/token.d.ts +2 -1
- package/lib/token.js +1 -0
- package/lib/tools.d.ts +9 -0
- package/lib/tools.js +75 -0
- package/lib/type.d.ts +64 -3
- package/lib/type.js +2 -0
- package/lib/websocket/websocket.d.ts +0 -0
- package/lib/websocket/websocket.js +152 -0
- package/package.json +8 -5
- package/src/document/commands.ts +91 -0
- package/src/document/iframePanel.ts +4 -1
- package/src/document/plugin.ts +28 -7
- package/src/document/toolbar.ts +39 -0
- package/src/document/widgetFactory.ts +17 -6
- package/src/global.d.ts +9 -0
- package/src/pythonServer/dash/dashServer.ts +66 -0
- package/src/pythonServer/index.ts +18 -0
- package/src/pythonServer/kernelExecutor.ts +243 -0
- package/src/pythonServer/streamlit/streamlitServer.ts +67 -0
- package/src/pythonServer/tornado/tornadoServer.ts +97 -0
- package/src/pythonWidget/comm.ts +65 -0
- package/src/pythonWidget/pythonWidget.ts +38 -3
- package/src/pythonWidget/pythonWidgetModel.ts +155 -34
- package/src/sandpackWidget/sandpackFilesModel.ts +17 -3
- package/src/sandpackWidget/sandpackPanel.ts +45 -4
- package/src/swConnection/index.ts +5 -2
- package/src/swConnection/mainConnectionManager.ts +121 -0
- package/src/swConnection/sw.ts +1 -1
- package/src/swConnection/{comm_manager.ts → swCommManager.ts} +6 -0
- package/src/token.ts +5 -1
- package/src/tools.ts +91 -0
- package/src/type.ts +76 -4
- package/src/websocket/websocket.ts +216 -0
- package/style/base.css +7 -0
- package/style/icons/autoreload.svg +16 -0
- package/style/icons/box.svg +12 -0
- package/style/icons/externallink.svg +10 -0
- package/lib/pythonWidget/connectionManager.js +0 -27
- package/lib/pythonWidget/kernelExecutor.d.ts +0 -27
- package/lib/pythonWidget/kernelExecutor.js +0 -104
- package/lib/swConnection/comm_manager.d.ts +0 -6
- package/lib/swConnection/connection_manager.d.ts +0 -18
- package/lib/swConnection/connection_manager.js +0 -27
- package/src/pythonWidget/connectionManager.ts +0 -43
- package/src/pythonWidget/kernelExecutor.ts +0 -140
- package/src/swConnection/connection_manager.ts +0 -43
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { stringOrNone } from '../../tools';
|
|
2
|
+
import { JupyterPackFramework } from '../../type';
|
|
3
|
+
import { KernelExecutor } from '../kernelExecutor';
|
|
4
|
+
export class TornadoServer extends KernelExecutor {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this._SERVER_VAR = '__jupyterpack_tornado_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.TORNADO
|
|
16
|
+
});
|
|
17
|
+
const bootstrapCode = `
|
|
18
|
+
from jupyterpack.common import set_base_url_env, patch_tornado
|
|
19
|
+
set_base_url_env("${baseURL}")
|
|
20
|
+
patch_tornado()
|
|
21
|
+
|
|
22
|
+
`;
|
|
23
|
+
await this.executeCode({ code: bootstrapCode });
|
|
24
|
+
if (initCode) {
|
|
25
|
+
const initCodeWithUrl = initCode.replaceAll('{{base_url}}', baseURL);
|
|
26
|
+
await this.executeCode({ code: initCodeWithUrl });
|
|
27
|
+
const loaderCode = `
|
|
28
|
+
from jupyterpack.tornado import TornadoServer
|
|
29
|
+
${this._SERVER_VAR} = TornadoServer(app, "${baseURL}")
|
|
30
|
+
`;
|
|
31
|
+
await this.executeCode({ code: loaderCode });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
getResponseFunctionFactory(options) {
|
|
35
|
+
const { method, urlPath, headers, params, content } = options;
|
|
36
|
+
const code = `await ${this._SERVER_VAR}.get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
|
|
37
|
+
return code;
|
|
38
|
+
}
|
|
39
|
+
openWebsocketFunctionFactory(options) {
|
|
40
|
+
const { instanceId, kernelId, wsUrl, protocol } = options;
|
|
41
|
+
const code = `await ${this._SERVER_VAR}.open_ws("${instanceId}", "${kernelId}", "${wsUrl}", ${stringOrNone(protocol)})`;
|
|
42
|
+
return code;
|
|
43
|
+
}
|
|
44
|
+
sendWebsocketMessageFunctionFactory(options) {
|
|
45
|
+
const { instanceId, kernelId, wsUrl, message } = options;
|
|
46
|
+
const code = `await ${this._SERVER_VAR}.receive_ws_message("${instanceId}", "${kernelId}", "${wsUrl}", '''${message}''')`;
|
|
47
|
+
return code;
|
|
48
|
+
}
|
|
49
|
+
async disposePythonServer() {
|
|
50
|
+
await this.executeCode({
|
|
51
|
+
code: `${this._SERVER_VAR}.dispose()`
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async reloadPythonServer(options) {
|
|
55
|
+
var _a;
|
|
56
|
+
const { initCode } = options;
|
|
57
|
+
if (initCode) {
|
|
58
|
+
await this.executeCode({
|
|
59
|
+
code: initCode.replaceAll('{{base_url}}', (_a = this._baseUrl) !== null && _a !== void 0 ? _a : '')
|
|
60
|
+
});
|
|
61
|
+
const reloadCode = `
|
|
62
|
+
${this._SERVER_VAR}.dispose()
|
|
63
|
+
${this._SERVER_VAR}.reload(app)
|
|
64
|
+
`;
|
|
65
|
+
await this.executeCode({ code: reloadCode }, true);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Kernel } from '@jupyterlab/services';
|
|
2
|
+
export declare class CommBroadcastManager {
|
|
3
|
+
constructor();
|
|
4
|
+
registerKernel(kernel: Kernel.IKernelConnection): void;
|
|
5
|
+
unregisterKernel(kernelId?: string): void;
|
|
6
|
+
private _handle_comm_open;
|
|
7
|
+
dispose(): void;
|
|
8
|
+
private _kernels;
|
|
9
|
+
private _comms;
|
|
10
|
+
private _broadcastChannels;
|
|
11
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const COMM_NAME = 'jupyterpack:broadcast:comm';
|
|
2
|
+
export class CommBroadcastManager {
|
|
3
|
+
constructor() {
|
|
4
|
+
this._handle_comm_open = async (comm, msg, kernelId) => {
|
|
5
|
+
var _a;
|
|
6
|
+
if (this._comms.has(kernelId)) {
|
|
7
|
+
(_a = this._comms.get(kernelId)) === null || _a === void 0 ? void 0 : _a.push(comm);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
this._comms.set(kernelId, [comm]);
|
|
11
|
+
}
|
|
12
|
+
const channelName = msg.metadata.channel_name;
|
|
13
|
+
if (!channelName) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!this._broadcastChannels.has(channelName)) {
|
|
17
|
+
this._broadcastChannels.set(channelName, new BroadcastChannel(channelName));
|
|
18
|
+
}
|
|
19
|
+
const broadcastChannel = this._broadcastChannels.get(channelName);
|
|
20
|
+
comm.onMsg = commMsg => {
|
|
21
|
+
const { data } = commMsg.content;
|
|
22
|
+
broadcastChannel.postMessage(data);
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
this._kernels = new Map();
|
|
26
|
+
this._comms = new Map();
|
|
27
|
+
this._broadcastChannels = new Map();
|
|
28
|
+
this._kernels = new Map();
|
|
29
|
+
this._comms = new Map();
|
|
30
|
+
}
|
|
31
|
+
registerKernel(kernel) {
|
|
32
|
+
this._kernels.set(kernel.id, kernel);
|
|
33
|
+
kernel.registerCommTarget(COMM_NAME, (comm, msg) => this._handle_comm_open(comm, msg, kernel.id));
|
|
34
|
+
}
|
|
35
|
+
unregisterKernel(kernelId) {
|
|
36
|
+
var _a;
|
|
37
|
+
if (kernelId) {
|
|
38
|
+
this._kernels.delete(kernelId);
|
|
39
|
+
const comms = (_a = this._comms.get(kernelId)) !== null && _a !== void 0 ? _a : [];
|
|
40
|
+
comms.forEach(comm => comm.dispose());
|
|
41
|
+
this._comms.delete(kernelId);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
dispose() {
|
|
45
|
+
this._kernels.clear();
|
|
46
|
+
this._comms.clear();
|
|
47
|
+
this._broadcastChannels.forEach(it => {
|
|
48
|
+
it.close();
|
|
49
|
+
});
|
|
50
|
+
this._broadcastChannels.clear();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -2,8 +2,14 @@ import { PythonWidgetModel } from './pythonWidgetModel';
|
|
|
2
2
|
import { IFramePanel } from '../document/iframePanel';
|
|
3
3
|
export declare class PythonWidget extends IFramePanel {
|
|
4
4
|
constructor(options: PythonWidget.IOptions);
|
|
5
|
+
get autoreload(): boolean;
|
|
6
|
+
set autoreload(value: boolean);
|
|
7
|
+
get isReady(): Promise<void>;
|
|
5
8
|
get model(): PythonWidgetModel;
|
|
9
|
+
reload(): Promise<void>;
|
|
10
|
+
dispose(): void;
|
|
6
11
|
private _model;
|
|
12
|
+
private _isReady;
|
|
7
13
|
}
|
|
8
14
|
export declare namespace PythonWidget {
|
|
9
15
|
interface IOptions {
|
|
@@ -1,22 +1,47 @@
|
|
|
1
|
-
import { PageConfig } from '@jupyterlab/coreutils';
|
|
1
|
+
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
2
2
|
import { IFramePanel } from '../document/iframePanel';
|
|
3
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
3
4
|
export class PythonWidget extends IFramePanel {
|
|
4
5
|
constructor(options) {
|
|
5
6
|
super();
|
|
7
|
+
this._isReady = new PromiseDelegate();
|
|
6
8
|
this._model = options.model;
|
|
7
9
|
this._model.initialize().then(connectionData => {
|
|
8
|
-
if (!connectionData) {
|
|
10
|
+
if (!connectionData.success) {
|
|
11
|
+
this.toggleSpinner(false);
|
|
12
|
+
this._iframe.contentDocument.body.innerText = `Failed to start server: ${connectionData.error}`;
|
|
9
13
|
return;
|
|
10
14
|
}
|
|
15
|
+
this._isReady.resolve();
|
|
11
16
|
const iframe = this._iframe;
|
|
12
17
|
const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
|
|
13
|
-
|
|
18
|
+
const iframeUrl = URLExt.join(fullLabextensionsUrl, 'jupyterpack/static', connectionData.instanceId, connectionData.framework, connectionData.kernelClientId, connectionData.rootUrl);
|
|
19
|
+
iframe.src = iframeUrl;
|
|
14
20
|
iframe.addEventListener('load', () => {
|
|
15
21
|
this.toggleSpinner(false);
|
|
16
22
|
});
|
|
23
|
+
this._model.serverReloaded.connect(() => {
|
|
24
|
+
var _a, _b, _c;
|
|
25
|
+
(_c = (_b = (_a = this._iframe) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.location) === null || _c === void 0 ? void 0 : _c.reload();
|
|
26
|
+
});
|
|
17
27
|
});
|
|
18
28
|
}
|
|
29
|
+
get autoreload() {
|
|
30
|
+
return this._model.autoreload;
|
|
31
|
+
}
|
|
32
|
+
set autoreload(value) {
|
|
33
|
+
this._model.autoreload = value;
|
|
34
|
+
}
|
|
35
|
+
get isReady() {
|
|
36
|
+
return this._isReady.promise;
|
|
37
|
+
}
|
|
19
38
|
get model() {
|
|
20
39
|
return this._model;
|
|
21
40
|
}
|
|
41
|
+
async reload() {
|
|
42
|
+
await this._model.reload();
|
|
43
|
+
}
|
|
44
|
+
dispose() {
|
|
45
|
+
this._model.dispose();
|
|
46
|
+
}
|
|
22
47
|
}
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { IConnectionManager } from '../type';
|
|
5
|
-
export declare class PythonWidgetModel implements
|
|
2
|
+
import { Contents, ServiceManager } from '@jupyterlab/services';
|
|
3
|
+
import { Signal } from '@lumino/signaling';
|
|
4
|
+
import { IConnectionManager, IJupyterPackFileFormat, IPythonWidgetModel, JupyterPackFramework } from '../type';
|
|
5
|
+
export declare class PythonWidgetModel implements IPythonWidgetModel {
|
|
6
6
|
constructor(options: PythonWidgetModel.IOptions);
|
|
7
|
+
get autoreload(): boolean;
|
|
8
|
+
set autoreload(val: boolean);
|
|
7
9
|
get isDisposed(): boolean;
|
|
8
10
|
get connectionManager(): IConnectionManager;
|
|
11
|
+
get serverReloaded(): Signal<IPythonWidgetModel, void>;
|
|
12
|
+
get kernelStatusChanged(): Signal<IPythonWidgetModel, "started" | "stopped">;
|
|
13
|
+
reload(): Promise<void>;
|
|
9
14
|
initialize(): Promise<{
|
|
15
|
+
success: true;
|
|
10
16
|
instanceId: string;
|
|
11
17
|
kernelClientId: string;
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
rootUrl: string;
|
|
19
|
+
framework: JupyterPackFramework;
|
|
20
|
+
} | {
|
|
21
|
+
success: false;
|
|
22
|
+
error: string;
|
|
23
|
+
}>;
|
|
24
|
+
dispose(): Promise<void>;
|
|
25
|
+
private _loadData;
|
|
26
|
+
private _onFileChanged;
|
|
27
|
+
private _commBroadcastManager;
|
|
14
28
|
private _isDisposed;
|
|
15
29
|
private _kernelStarted;
|
|
16
30
|
private _sessionConnection;
|
|
@@ -18,9 +32,16 @@ export declare class PythonWidgetModel implements IDisposable {
|
|
|
18
32
|
private _context;
|
|
19
33
|
private _connectionManager;
|
|
20
34
|
private _contentsManager;
|
|
35
|
+
private _jpackModel;
|
|
36
|
+
private _executor?;
|
|
37
|
+
private _localPath;
|
|
38
|
+
private _serverReloaded;
|
|
39
|
+
private _kernelStatusChanged;
|
|
40
|
+
private _autoreload;
|
|
21
41
|
}
|
|
22
42
|
export declare namespace PythonWidgetModel {
|
|
23
43
|
interface IOptions {
|
|
44
|
+
jpackModel: IJupyterPackFileFormat;
|
|
24
45
|
context: DocumentRegistry.IContext<DocumentRegistry.IModel>;
|
|
25
46
|
manager: ServiceManager.IManager;
|
|
26
47
|
connectionManager: IConnectionManager;
|
|
@@ -1,14 +1,31 @@
|
|
|
1
|
-
import { PromiseDelegate } from '@lumino/coreutils';
|
|
2
|
-
import { KernelExecutor } from './kernelExecutor';
|
|
3
1
|
import { PathExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
3
|
+
import { PYTHON_SERVER } from '../pythonServer';
|
|
4
|
+
import { Signal } from '@lumino/signaling';
|
|
5
|
+
import { CommBroadcastManager } from './comm';
|
|
6
|
+
import { IS_LITE } from '../tools';
|
|
4
7
|
export class PythonWidgetModel {
|
|
5
8
|
constructor(options) {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
this._commBroadcastManager = new CommBroadcastManager();
|
|
6
11
|
this._isDisposed = false;
|
|
7
12
|
this._kernelStarted = false;
|
|
13
|
+
this._serverReloaded = new Signal(this);
|
|
14
|
+
this._kernelStatusChanged = new Signal(this);
|
|
8
15
|
this._context = options.context;
|
|
9
16
|
this._manager = options.manager;
|
|
10
17
|
this._connectionManager = options.connectionManager;
|
|
11
18
|
this._contentsManager = options.contentsManager;
|
|
19
|
+
this._jpackModel = options.jpackModel;
|
|
20
|
+
this._localPath = PathExt.dirname(this._context.localPath);
|
|
21
|
+
this._autoreload = Boolean((_b = (_a = this._jpackModel) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.autoreload);
|
|
22
|
+
this._contentsManager.fileChanged.connect(this._onFileChanged, this);
|
|
23
|
+
}
|
|
24
|
+
get autoreload() {
|
|
25
|
+
return this._autoreload;
|
|
26
|
+
}
|
|
27
|
+
set autoreload(val) {
|
|
28
|
+
this._autoreload = val;
|
|
12
29
|
}
|
|
13
30
|
get isDisposed() {
|
|
14
31
|
return this._isDisposed;
|
|
@@ -16,24 +33,42 @@ export class PythonWidgetModel {
|
|
|
16
33
|
get connectionManager() {
|
|
17
34
|
return this._connectionManager;
|
|
18
35
|
}
|
|
36
|
+
get serverReloaded() {
|
|
37
|
+
return this._serverReloaded;
|
|
38
|
+
}
|
|
39
|
+
get kernelStatusChanged() {
|
|
40
|
+
return this._kernelStatusChanged;
|
|
41
|
+
}
|
|
42
|
+
async reload() {
|
|
43
|
+
var _a;
|
|
44
|
+
if (!this._kernelStarted) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const { spkContent, entryContent } = await this._loadData();
|
|
48
|
+
await ((_a = this._executor) === null || _a === void 0 ? void 0 : _a.reloadPythonServer({
|
|
49
|
+
entryPath: spkContent.entry,
|
|
50
|
+
initCode: entryContent.content
|
|
51
|
+
}));
|
|
52
|
+
this._serverReloaded.emit();
|
|
53
|
+
}
|
|
19
54
|
async initialize() {
|
|
20
55
|
var _a;
|
|
21
56
|
if (this._kernelStarted) {
|
|
22
|
-
return
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: 'Server is called twice'
|
|
60
|
+
};
|
|
23
61
|
}
|
|
24
|
-
const filePath = this.
|
|
25
|
-
const spkContent = this._context.model.toJSON();
|
|
26
|
-
const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
|
|
27
|
-
const entryContent = await this._contentsManager.get(entryPath, {
|
|
28
|
-
content: true,
|
|
29
|
-
format: 'text'
|
|
30
|
-
});
|
|
62
|
+
const { filePath, spkContent, rootUrl, entryContent } = await this._loadData();
|
|
31
63
|
const sessionManager = this._manager.sessions;
|
|
32
64
|
await sessionManager.ready;
|
|
33
65
|
await this._manager.kernelspecs.ready;
|
|
34
66
|
const specs = this._manager.kernelspecs.specs;
|
|
35
67
|
if (!specs) {
|
|
36
|
-
return
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: 'Missing kernel spec'
|
|
71
|
+
};
|
|
37
72
|
}
|
|
38
73
|
const { kernelspecs } = specs;
|
|
39
74
|
let kernelName = Object.keys(kernelspecs)[0];
|
|
@@ -47,13 +82,33 @@ export class PythonWidgetModel {
|
|
|
47
82
|
name: kernelName
|
|
48
83
|
},
|
|
49
84
|
type: 'notebook'
|
|
85
|
+
}, {
|
|
86
|
+
kernelConnectionOptions: { handleComms: true }
|
|
50
87
|
});
|
|
51
|
-
const
|
|
88
|
+
const kernel = this._sessionConnection.kernel;
|
|
89
|
+
if (kernel) {
|
|
90
|
+
this._kernelStatusChanged.emit('started');
|
|
91
|
+
this._commBroadcastManager.registerKernel(kernel);
|
|
92
|
+
kernel.disposed.connect(() => {
|
|
93
|
+
this._kernelStatusChanged.emit('stopped');
|
|
94
|
+
this._commBroadcastManager.unregisterKernel(kernel.id);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const framework = spkContent.framework;
|
|
98
|
+
const ServerClass = PYTHON_SERVER.get(framework);
|
|
99
|
+
if (!ServerClass) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: `Framework "${framework}" is not supported. Please check your .spk file.`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const executor = (this._executor = new ServerClass({
|
|
52
106
|
sessionConnection: this._sessionConnection
|
|
53
|
-
});
|
|
107
|
+
}));
|
|
54
108
|
const data = await this._connectionManager.registerConnection(executor);
|
|
55
109
|
await executor.init({
|
|
56
110
|
initCode: entryContent.content,
|
|
111
|
+
entryPath: spkContent.entry,
|
|
57
112
|
...data
|
|
58
113
|
});
|
|
59
114
|
const finish = new PromiseDelegate();
|
|
@@ -67,9 +122,40 @@ export class PythonWidgetModel {
|
|
|
67
122
|
(_a = this._sessionConnection.kernel) === null || _a === void 0 ? void 0 : _a.statusChanged.connect(cb);
|
|
68
123
|
await finish.promise;
|
|
69
124
|
this._kernelStarted = true;
|
|
70
|
-
return data;
|
|
125
|
+
return { ...data, rootUrl, framework, success: true };
|
|
71
126
|
}
|
|
72
|
-
dispose() {
|
|
127
|
+
async dispose() {
|
|
128
|
+
var _a, _b, _c;
|
|
129
|
+
if (this._isDisposed) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!IS_LITE) {
|
|
133
|
+
(_b = (_a = this._sessionConnection) === null || _a === void 0 ? void 0 : _a.kernel) === null || _b === void 0 ? void 0 : _b.shutdown();
|
|
134
|
+
}
|
|
135
|
+
void ((_c = this._executor) === null || _c === void 0 ? void 0 : _c.disposePythonServer());
|
|
136
|
+
this._contentsManager.fileChanged.disconnect(this._onFileChanged);
|
|
137
|
+
this._commBroadcastManager.dispose();
|
|
73
138
|
this._isDisposed = true;
|
|
74
139
|
}
|
|
140
|
+
async _loadData() {
|
|
141
|
+
var _a;
|
|
142
|
+
const filePath = this._context.localPath;
|
|
143
|
+
const spkContent = this._jpackModel;
|
|
144
|
+
const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
|
|
145
|
+
const rootUrl = (_a = spkContent.rootUrl) !== null && _a !== void 0 ? _a : '/';
|
|
146
|
+
const entryContent = await this._contentsManager.get(entryPath, {
|
|
147
|
+
content: true,
|
|
148
|
+
format: 'text'
|
|
149
|
+
});
|
|
150
|
+
return { filePath, spkContent, rootUrl, entryContent };
|
|
151
|
+
}
|
|
152
|
+
async _onFileChanged(sender, args) {
|
|
153
|
+
var _a;
|
|
154
|
+
if (this._autoreload && args.type === 'save') {
|
|
155
|
+
if (((_a = args.newValue) === null || _a === void 0 ? void 0 : _a.path) &&
|
|
156
|
+
args.newValue.path.startsWith(this._localPath)) {
|
|
157
|
+
await this.reload();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
75
161
|
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { Contents } from '@jupyterlab/services';
|
|
2
2
|
import { ISignal } from '@lumino/signaling';
|
|
3
3
|
import { IDict } from '../type';
|
|
4
|
-
|
|
4
|
+
import { IDisposable } from '@lumino/disposable';
|
|
5
|
+
export declare class SandpackFilesModel implements IDisposable {
|
|
5
6
|
constructor(options: {
|
|
6
7
|
contentsManager: Contents.IManager;
|
|
7
8
|
path: string;
|
|
8
9
|
});
|
|
9
|
-
getAllFiles(): Promise<IDict<{
|
|
10
|
+
getAllFiles(force?: boolean): Promise<IDict<{
|
|
10
11
|
code: string;
|
|
11
12
|
}>>;
|
|
13
|
+
get isDisposed(): boolean;
|
|
12
14
|
get fileChanged(): ISignal<SandpackFilesModel, {
|
|
13
15
|
allFiles: IDict<{
|
|
14
16
|
code: string;
|
|
15
17
|
}>;
|
|
16
18
|
}>;
|
|
19
|
+
dispose(): void;
|
|
17
20
|
flattenDirectory(dirContent: Contents.IModel): Promise<IDict<{
|
|
18
21
|
code: string;
|
|
19
22
|
}>>;
|
|
@@ -24,4 +27,5 @@ export declare class SandpackFilesModel {
|
|
|
24
27
|
private _fileChanged;
|
|
25
28
|
private _contentManager;
|
|
26
29
|
private _allFiles?;
|
|
30
|
+
private _isDisposed;
|
|
27
31
|
}
|
|
@@ -3,12 +3,13 @@ import { removePrefix } from '../tools';
|
|
|
3
3
|
export class SandpackFilesModel {
|
|
4
4
|
constructor(options) {
|
|
5
5
|
this._fileChanged = new Signal(this);
|
|
6
|
+
this._isDisposed = false;
|
|
6
7
|
this._contentManager = options.contentsManager;
|
|
7
8
|
this._path = options.path;
|
|
8
9
|
this._contentManager.fileChanged.connect(this._onFileChanged, this);
|
|
9
10
|
}
|
|
10
|
-
async getAllFiles() {
|
|
11
|
-
if (!this._allFiles) {
|
|
11
|
+
async getAllFiles(force = false) {
|
|
12
|
+
if (!this._allFiles || force) {
|
|
12
13
|
const files = await this._contentManager.get(this._path, {
|
|
13
14
|
content: true
|
|
14
15
|
});
|
|
@@ -16,9 +17,19 @@ export class SandpackFilesModel {
|
|
|
16
17
|
}
|
|
17
18
|
return this._allFiles;
|
|
18
19
|
}
|
|
20
|
+
get isDisposed() {
|
|
21
|
+
return this._isDisposed;
|
|
22
|
+
}
|
|
19
23
|
get fileChanged() {
|
|
20
24
|
return this._fileChanged;
|
|
21
25
|
}
|
|
26
|
+
dispose() {
|
|
27
|
+
if (this._isDisposed) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this._isDisposed = true;
|
|
31
|
+
this._contentManager.fileChanged.disconnect(this._onFileChanged);
|
|
32
|
+
}
|
|
22
33
|
async flattenDirectory(dirContent) {
|
|
23
34
|
const flatDict = {};
|
|
24
35
|
const content = dirContent.content;
|
|
@@ -2,15 +2,24 @@ import { SandpackClient } from '@codesandbox/sandpack-client';
|
|
|
2
2
|
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
3
3
|
import { Contents } from '@jupyterlab/services';
|
|
4
4
|
import { IFramePanel } from '../document/iframePanel';
|
|
5
|
+
import { IJupyterPackFileFormat } from '../type';
|
|
5
6
|
import { SandpackFilesModel } from './sandpackFilesModel';
|
|
6
7
|
export declare class SandpackPanel extends IFramePanel {
|
|
7
8
|
constructor(options: {
|
|
8
9
|
context: DocumentRegistry.IContext<DocumentRegistry.IModel>;
|
|
9
10
|
contentsManager: Contents.IManager;
|
|
10
11
|
});
|
|
11
|
-
|
|
12
|
+
dispose(): void;
|
|
13
|
+
get isReady(): Promise<void>;
|
|
14
|
+
get autoreload(): boolean;
|
|
15
|
+
set autoreload(val: boolean);
|
|
16
|
+
init(localPath: string, jpackModel: IJupyterPackFileFormat): Promise<void>;
|
|
12
17
|
connectSignals(filesModel: SandpackFilesModel, sandpackClient: SandpackClient): Promise<void>;
|
|
18
|
+
reload(): Promise<void>;
|
|
13
19
|
private _onFileChanged;
|
|
14
20
|
private _spClient?;
|
|
15
21
|
private _contentsManager;
|
|
22
|
+
private _fileModel;
|
|
23
|
+
private _autoreload;
|
|
24
|
+
private _isReady;
|
|
16
25
|
}
|
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
import { loadSandpackClient } from '@codesandbox/sandpack-client';
|
|
2
2
|
import { IFramePanel } from '../document/iframePanel';
|
|
3
3
|
import { SandpackFilesModel } from './sandpackFilesModel';
|
|
4
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
4
5
|
export class SandpackPanel extends IFramePanel {
|
|
5
6
|
constructor(options) {
|
|
6
7
|
super();
|
|
8
|
+
this._autoreload = false;
|
|
9
|
+
this._isReady = new PromiseDelegate();
|
|
7
10
|
this._contentsManager = options.contentsManager;
|
|
11
|
+
const { context } = options;
|
|
8
12
|
options.context.ready.then(async () => {
|
|
9
|
-
|
|
13
|
+
const jpackModel = context.model.toJSON();
|
|
14
|
+
await this.init(context.localPath, jpackModel);
|
|
10
15
|
});
|
|
11
16
|
}
|
|
12
|
-
|
|
17
|
+
dispose() {
|
|
18
|
+
var _a, _b, _c;
|
|
19
|
+
(_a = this._fileModel) === null || _a === void 0 ? void 0 : _a.fileChanged.disconnect(this._onFileChanged);
|
|
20
|
+
(_b = this._fileModel) === null || _b === void 0 ? void 0 : _b.dispose();
|
|
21
|
+
(_c = this._spClient) === null || _c === void 0 ? void 0 : _c.destroy();
|
|
22
|
+
super.dispose();
|
|
23
|
+
}
|
|
24
|
+
get isReady() {
|
|
25
|
+
return this._isReady.promise;
|
|
26
|
+
}
|
|
27
|
+
get autoreload() {
|
|
28
|
+
return this._autoreload;
|
|
29
|
+
}
|
|
30
|
+
set autoreload(val) {
|
|
31
|
+
this._autoreload = val;
|
|
32
|
+
}
|
|
33
|
+
async init(localPath, jpackModel) {
|
|
34
|
+
var _a;
|
|
35
|
+
if (((_a = jpackModel === null || jpackModel === void 0 ? void 0 : jpackModel.metadata) === null || _a === void 0 ? void 0 : _a.autoreload) === true) {
|
|
36
|
+
this._autoreload = true;
|
|
37
|
+
}
|
|
13
38
|
const currentDir = localPath.split('/').slice(0, -1).join('/');
|
|
14
39
|
const filesModel = new SandpackFilesModel({
|
|
15
40
|
contentsManager: this._contentsManager,
|
|
16
41
|
path: currentDir
|
|
17
42
|
});
|
|
43
|
+
this._fileModel = filesModel;
|
|
18
44
|
const allFiles = await filesModel.getAllFiles();
|
|
19
45
|
const options = {
|
|
20
46
|
showLoadingScreen: true,
|
|
@@ -24,6 +50,7 @@ export class SandpackPanel extends IFramePanel {
|
|
|
24
50
|
files: allFiles
|
|
25
51
|
}, options);
|
|
26
52
|
await this.connectSignals(filesModel, this._spClient);
|
|
53
|
+
this._isReady.resolve();
|
|
27
54
|
}
|
|
28
55
|
async connectSignals(filesModel, sandpackClient) {
|
|
29
56
|
filesModel.fileChanged.connect(this._onFileChanged, this);
|
|
@@ -42,8 +69,16 @@ export class SandpackPanel extends IFramePanel {
|
|
|
42
69
|
}
|
|
43
70
|
});
|
|
44
71
|
}
|
|
72
|
+
async reload() {
|
|
73
|
+
if (this._spClient && this._fileModel) {
|
|
74
|
+
const allFiles = await this._fileModel.getAllFiles();
|
|
75
|
+
this._spClient.updateSandbox({
|
|
76
|
+
files: allFiles
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
45
80
|
_onFileChanged(sender, { allFiles }) {
|
|
46
|
-
if (this._spClient) {
|
|
81
|
+
if (this._autoreload && this._spClient) {
|
|
47
82
|
this._spClient.updateSandbox({
|
|
48
83
|
files: allFiles
|
|
49
84
|
});
|
|
@@ -3,7 +3,7 @@ import { UUID } from '@lumino/coreutils';
|
|
|
3
3
|
import { expose } from 'comlink';
|
|
4
4
|
import { IConnectionManagerToken } from '../token';
|
|
5
5
|
import { MessageAction } from '../type';
|
|
6
|
-
import { ConnectionManager } from './
|
|
6
|
+
import { ConnectionManager } from './mainConnectionManager';
|
|
7
7
|
const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
|
|
8
8
|
const SCOPE = `${fullLabextensionsUrl}/jupyterpack/static`;
|
|
9
9
|
async function initServiceWorker() {
|
|
@@ -50,12 +50,12 @@ export const swPlugin = {
|
|
|
50
50
|
autoStart: true,
|
|
51
51
|
provides: IConnectionManagerToken,
|
|
52
52
|
activate: async (app) => {
|
|
53
|
-
console.log('Activating jupyterpack service worker');
|
|
54
53
|
const serviceWorker = await initServiceWorker();
|
|
55
54
|
if (!serviceWorker) {
|
|
56
55
|
throw new Error('Failed to register the Service Worker, please make sure to use a browser that supports this feature.');
|
|
57
56
|
}
|
|
58
57
|
const instanceId = UUID.uuid4();
|
|
58
|
+
console.log('Activating jupyterpack service worker with instance id', instanceId);
|
|
59
59
|
const { port1: mainToServiceWorker, port2: serviceWorkerToMain } = new MessageChannel();
|
|
60
60
|
const connectionManager = new ConnectionManager(instanceId);
|
|
61
61
|
expose(connectionManager, mainToServiceWorker);
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { IConnectionManager, IDict, IKernelExecutor } from '../type';
|
|
2
|
+
/**
|
|
3
|
+
* Manages connections between clients and kernel executors.
|
|
4
|
+
* This class handles the registration of kernel executors and the generation of responses
|
|
5
|
+
* for client requests. It maintains a mapping of kernel client IDs to their respective executors.
|
|
6
|
+
* The HTTP requests intercepted by the service worker are forwarded to the appropriate kernel executor.
|
|
7
|
+
* The websocket messages forwarded from the broadcast channel are also forwarded to the appropriate kernel executor.
|
|
8
|
+
* It's running on the main thread
|
|
9
|
+
*/
|
|
2
10
|
export declare class ConnectionManager implements IConnectionManager {
|
|
3
11
|
instanceId: string;
|
|
4
12
|
constructor(instanceId: string);
|
|
@@ -14,5 +22,7 @@ export declare class ConnectionManager implements IConnectionManager {
|
|
|
14
22
|
requestBody?: ArrayBuffer;
|
|
15
23
|
params?: string;
|
|
16
24
|
}): Promise<IDict | null>;
|
|
25
|
+
private _initWsChannel;
|
|
17
26
|
private _kernelExecutors;
|
|
27
|
+
private _wsBroadcastChannel;
|
|
18
28
|
}
|