jupyterpack 0.3.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/document/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 +14 -6
- package/lib/index.d.ts +1 -1
- package/lib/pythonServer/baseServer.d.ts +82 -0
- package/lib/pythonServer/baseServer.js +141 -0
- package/lib/pythonServer/dash/dashServer.d.ts +5 -17
- package/lib/pythonServer/dash/dashServer.js +19 -24
- package/lib/pythonServer/index.d.ts +3 -3
- package/lib/pythonServer/index.js +5 -1
- package/lib/pythonServer/kernelExecutor.d.ts +2 -53
- package/lib/pythonServer/kernelExecutor.js +5 -70
- package/lib/pythonServer/shiny/shinyServer.d.ts +14 -0
- package/lib/pythonServer/shiny/shinyServer.js +49 -0
- package/lib/pythonServer/starlette/starletteServer.d.ts +13 -0
- package/lib/pythonServer/starlette/starletteServer.js +49 -0
- package/lib/pythonServer/streamlit/streamlitServer.d.ts +6 -26
- package/lib/pythonServer/streamlit/streamlitServer.js +33 -41
- package/lib/pythonServer/tornado/tornadoServer.d.ts +6 -26
- package/lib/pythonServer/tornado/tornadoServer.js +28 -36
- package/lib/pythonWidget/comm.d.ts +11 -0
- package/lib/pythonWidget/comm.js +52 -0
- package/lib/pythonWidget/pythonWidget.d.ts +5 -0
- package/lib/pythonWidget/pythonWidget.js +19 -0
- package/lib/pythonWidget/pythonWidgetModel.d.ts +16 -4
- package/lib/pythonWidget/pythonWidgetModel.js +77 -13
- 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/mainConnectionManager.d.ts +4 -4
- package/lib/swConnection/mainConnectionManager.js +23 -12
- package/lib/token.d.ts +2 -1
- package/lib/token.js +1 -0
- package/lib/tools.d.ts +6 -0
- package/lib/tools.js +25 -0
- package/lib/type.d.ts +39 -4
- package/lib/type.js +2 -0
- package/lib/websocket/websocket.js +5 -1
- package/package.json +6 -6
- 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 +16 -6
- package/src/global.d.ts +5 -0
- package/src/pythonServer/baseServer.ts +245 -0
- package/src/pythonServer/dash/dashServer.ts +25 -35
- package/src/pythonServer/index.ts +9 -5
- package/src/pythonServer/kernelExecutor.ts +8 -147
- package/src/pythonServer/shiny/shinyServer.ts +62 -0
- package/src/pythonServer/starlette/starletteServer.ts +59 -0
- package/src/pythonServer/streamlit/streamlitServer.ts +40 -62
- package/src/pythonServer/tornado/tornadoServer.ts +33 -60
- package/src/pythonWidget/comm.ts +65 -0
- package/src/pythonWidget/pythonWidget.ts +19 -1
- package/src/pythonWidget/pythonWidgetModel.ts +107 -20
- package/src/sandpackWidget/sandpackFilesModel.ts +17 -3
- package/src/sandpackWidget/sandpackPanel.ts +45 -4
- package/src/swConnection/mainConnectionManager.ts +28 -20
- package/src/token.ts +5 -1
- package/src/tools.ts +31 -0
- package/src/type.ts +46 -7
- package/src/websocket/websocket.ts +9 -1
- 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/pythonServer/common/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/common/generatedPythonFiles.js +0 -72
- package/lib/pythonServer/dash/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/dash/generatedPythonFiles.js +0 -31
- package/lib/pythonServer/streamlit/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/streamlit/generatedPythonFiles.js +0 -147
- package/lib/pythonServer/tornado/generatedPythonFiles.d.ts +0 -3
- package/lib/pythonServer/tornado/generatedPythonFiles.js +0 -456
- package/src/pythonServer/common/generatedPythonFiles.ts +0 -73
- package/src/pythonServer/dash/generatedPythonFiles.ts +0 -32
- package/src/pythonServer/streamlit/generatedPythonFiles.ts +0 -148
- package/src/pythonServer/tornado/generatedPythonFiles.ts +0 -457
|
@@ -7,31 +7,64 @@ import {
|
|
|
7
7
|
Session
|
|
8
8
|
} from '@jupyterlab/services';
|
|
9
9
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
10
|
-
import { IDisposable } from '@lumino/disposable';
|
|
11
10
|
|
|
12
11
|
import { PYTHON_SERVER } from '../pythonServer';
|
|
12
|
+
import { Signal } from '@lumino/signaling';
|
|
13
13
|
import {
|
|
14
|
+
IBasePythonServer,
|
|
14
15
|
IConnectionManager,
|
|
15
16
|
IJupyterPackFileFormat,
|
|
16
|
-
|
|
17
|
+
IPythonWidgetModel,
|
|
17
18
|
JupyterPackFramework
|
|
18
19
|
} from '../type';
|
|
20
|
+
import { CommBroadcastManager } from './comm';
|
|
21
|
+
import { IS_LITE } from '../tools';
|
|
19
22
|
|
|
20
|
-
export class PythonWidgetModel implements
|
|
23
|
+
export class PythonWidgetModel implements IPythonWidgetModel {
|
|
21
24
|
constructor(options: PythonWidgetModel.IOptions) {
|
|
22
25
|
this._context = options.context;
|
|
23
26
|
this._manager = options.manager;
|
|
24
27
|
this._connectionManager = options.connectionManager;
|
|
25
28
|
this._contentsManager = options.contentsManager;
|
|
26
29
|
this._jpackModel = options.jpackModel;
|
|
30
|
+
this._localPath = PathExt.dirname(this._context.localPath);
|
|
31
|
+
this._autoreload = Boolean(this._jpackModel?.metadata?.autoreload);
|
|
32
|
+
|
|
33
|
+
this._contentsManager.fileChanged.connect(this._onFileChanged, this);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get autoreload() {
|
|
37
|
+
return this._autoreload;
|
|
27
38
|
}
|
|
28
39
|
|
|
40
|
+
set autoreload(val: boolean) {
|
|
41
|
+
this._autoreload = val;
|
|
42
|
+
}
|
|
29
43
|
get isDisposed(): boolean {
|
|
30
44
|
return this._isDisposed;
|
|
31
45
|
}
|
|
32
46
|
get connectionManager(): IConnectionManager {
|
|
33
47
|
return this._connectionManager;
|
|
34
48
|
}
|
|
49
|
+
get serverReloaded() {
|
|
50
|
+
return this._serverReloaded;
|
|
51
|
+
}
|
|
52
|
+
get kernelStatusChanged() {
|
|
53
|
+
return this._kernelStatusChanged;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async reload() {
|
|
57
|
+
if (!this._kernelStarted) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const { spkContent, entryContent } = await this._loadData();
|
|
61
|
+
await this._executor?.reloadPythonServer({
|
|
62
|
+
entryPath: spkContent.entry,
|
|
63
|
+
initCode: entryContent.content
|
|
64
|
+
});
|
|
65
|
+
this._serverReloaded.emit();
|
|
66
|
+
}
|
|
67
|
+
|
|
35
68
|
async initialize(): Promise<
|
|
36
69
|
| {
|
|
37
70
|
success: true;
|
|
@@ -48,15 +81,9 @@ export class PythonWidgetModel implements IDisposable {
|
|
|
48
81
|
error: 'Server is called twice'
|
|
49
82
|
};
|
|
50
83
|
}
|
|
51
|
-
const filePath = this._context.localPath;
|
|
52
|
-
const spkContent = this._jpackModel;
|
|
53
84
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const entryContent = await this._contentsManager.get(entryPath, {
|
|
57
|
-
content: true,
|
|
58
|
-
format: 'text'
|
|
59
|
-
});
|
|
85
|
+
const { filePath, spkContent, rootUrl, entryContent } =
|
|
86
|
+
await this._loadData();
|
|
60
87
|
const sessionManager = this._manager.sessions;
|
|
61
88
|
await sessionManager.ready;
|
|
62
89
|
await this._manager.kernelspecs.ready;
|
|
@@ -73,14 +100,29 @@ export class PythonWidgetModel implements IDisposable {
|
|
|
73
100
|
kernelName = specs.default;
|
|
74
101
|
}
|
|
75
102
|
|
|
76
|
-
this._sessionConnection = await sessionManager.startNew(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
103
|
+
this._sessionConnection = await sessionManager.startNew(
|
|
104
|
+
{
|
|
105
|
+
name: filePath,
|
|
106
|
+
path: filePath,
|
|
107
|
+
kernel: {
|
|
108
|
+
name: kernelName
|
|
109
|
+
},
|
|
110
|
+
type: 'notebook'
|
|
81
111
|
},
|
|
82
|
-
|
|
83
|
-
|
|
112
|
+
{
|
|
113
|
+
kernelConnectionOptions: { handleComms: true }
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
const kernel = this._sessionConnection.kernel;
|
|
117
|
+
if (kernel) {
|
|
118
|
+
this._kernelStatusChanged.emit('started');
|
|
119
|
+
this._commBroadcastManager.registerKernel(kernel);
|
|
120
|
+
kernel.disposed.connect(() => {
|
|
121
|
+
this._kernelStatusChanged.emit('stopped');
|
|
122
|
+
this._commBroadcastManager.unregisterKernel(kernel.id);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
84
126
|
const framework = spkContent.framework;
|
|
85
127
|
const ServerClass = PYTHON_SERVER.get(framework);
|
|
86
128
|
if (!ServerClass) {
|
|
@@ -111,14 +153,48 @@ export class PythonWidgetModel implements IDisposable {
|
|
|
111
153
|
this._kernelStarted = true;
|
|
112
154
|
return { ...data, rootUrl, framework, success: true };
|
|
113
155
|
}
|
|
114
|
-
dispose(): void {
|
|
156
|
+
async dispose(): Promise<void> {
|
|
115
157
|
if (this._isDisposed) {
|
|
116
158
|
return;
|
|
117
159
|
}
|
|
160
|
+
if (!IS_LITE) {
|
|
161
|
+
this._sessionConnection?.kernel?.shutdown();
|
|
162
|
+
}
|
|
118
163
|
void this._executor?.disposePythonServer();
|
|
164
|
+
this._contentsManager.fileChanged.disconnect(this._onFileChanged);
|
|
165
|
+
this._commBroadcastManager.dispose();
|
|
119
166
|
this._isDisposed = true;
|
|
120
167
|
}
|
|
121
168
|
|
|
169
|
+
private async _loadData() {
|
|
170
|
+
const filePath = this._context.localPath;
|
|
171
|
+
const spkContent = this._jpackModel;
|
|
172
|
+
|
|
173
|
+
const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
|
|
174
|
+
const rootUrl = spkContent.rootUrl ?? '/';
|
|
175
|
+
const entryContent = await this._contentsManager.get(entryPath, {
|
|
176
|
+
content: true,
|
|
177
|
+
format: 'text'
|
|
178
|
+
});
|
|
179
|
+
return { filePath, spkContent, rootUrl, entryContent };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private async _onFileChanged(
|
|
183
|
+
sender: Contents.IManager,
|
|
184
|
+
args: Contents.IChangedArgs
|
|
185
|
+
) {
|
|
186
|
+
if (this._autoreload && args.type === 'save') {
|
|
187
|
+
if (
|
|
188
|
+
args.newValue?.path &&
|
|
189
|
+
args.newValue.path.startsWith(this._localPath)
|
|
190
|
+
) {
|
|
191
|
+
await this.reload();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private _commBroadcastManager = new CommBroadcastManager();
|
|
197
|
+
|
|
122
198
|
private _isDisposed = false;
|
|
123
199
|
private _kernelStarted = false;
|
|
124
200
|
private _sessionConnection: Session.ISessionConnection | undefined;
|
|
@@ -127,7 +203,18 @@ export class PythonWidgetModel implements IDisposable {
|
|
|
127
203
|
private _connectionManager: IConnectionManager;
|
|
128
204
|
private _contentsManager: Contents.IManager;
|
|
129
205
|
private _jpackModel: IJupyterPackFileFormat;
|
|
130
|
-
private _executor?:
|
|
206
|
+
private _executor?: IBasePythonServer;
|
|
207
|
+
private _localPath: string;
|
|
208
|
+
|
|
209
|
+
private _serverReloaded: Signal<IPythonWidgetModel, void> = new Signal<
|
|
210
|
+
IPythonWidgetModel,
|
|
211
|
+
void
|
|
212
|
+
>(this);
|
|
213
|
+
private _kernelStatusChanged: Signal<
|
|
214
|
+
IPythonWidgetModel,
|
|
215
|
+
'started' | 'stopped'
|
|
216
|
+
> = new Signal(this);
|
|
217
|
+
private _autoreload: boolean;
|
|
131
218
|
}
|
|
132
219
|
|
|
133
220
|
export namespace PythonWidgetModel {
|
|
@@ -3,16 +3,17 @@ import { ISignal, Signal } from '@lumino/signaling';
|
|
|
3
3
|
|
|
4
4
|
import { IDict } from '../type';
|
|
5
5
|
import { removePrefix } from '../tools';
|
|
6
|
+
import { IDisposable } from '@lumino/disposable';
|
|
6
7
|
|
|
7
|
-
export class SandpackFilesModel {
|
|
8
|
+
export class SandpackFilesModel implements IDisposable {
|
|
8
9
|
constructor(options: { contentsManager: Contents.IManager; path: string }) {
|
|
9
10
|
this._contentManager = options.contentsManager;
|
|
10
11
|
this._path = options.path;
|
|
11
12
|
this._contentManager.fileChanged.connect(this._onFileChanged, this);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
async getAllFiles(): Promise<IDict<{ code: string }>> {
|
|
15
|
-
if (!this._allFiles) {
|
|
15
|
+
async getAllFiles(force = false): Promise<IDict<{ code: string }>> {
|
|
16
|
+
if (!this._allFiles || force) {
|
|
16
17
|
const files = await this._contentManager.get(this._path, {
|
|
17
18
|
content: true
|
|
18
19
|
});
|
|
@@ -21,6 +22,9 @@ export class SandpackFilesModel {
|
|
|
21
22
|
|
|
22
23
|
return this._allFiles;
|
|
23
24
|
}
|
|
25
|
+
get isDisposed(): boolean {
|
|
26
|
+
return this._isDisposed;
|
|
27
|
+
}
|
|
24
28
|
|
|
25
29
|
get fileChanged(): ISignal<
|
|
26
30
|
SandpackFilesModel,
|
|
@@ -29,6 +33,14 @@ export class SandpackFilesModel {
|
|
|
29
33
|
return this._fileChanged;
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
dispose(): void {
|
|
37
|
+
if (this._isDisposed) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this._isDisposed = true;
|
|
41
|
+
this._contentManager.fileChanged.disconnect(this._onFileChanged);
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
async flattenDirectory(
|
|
33
45
|
dirContent: Contents.IModel
|
|
34
46
|
): Promise<IDict<{ code: string }>> {
|
|
@@ -138,4 +150,6 @@ export class SandpackFilesModel {
|
|
|
138
150
|
private _contentManager: Contents.IManager;
|
|
139
151
|
|
|
140
152
|
private _allFiles?: IDict<{ code: string }>;
|
|
153
|
+
|
|
154
|
+
private _isDisposed = false;
|
|
141
155
|
}
|
|
@@ -7,8 +7,9 @@ import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
|
7
7
|
import { Contents } from '@jupyterlab/services';
|
|
8
8
|
|
|
9
9
|
import { IFramePanel } from '../document/iframePanel';
|
|
10
|
-
import { IDict } from '../type';
|
|
10
|
+
import { IDict, IJupyterPackFileFormat } from '../type';
|
|
11
11
|
import { SandpackFilesModel } from './sandpackFilesModel';
|
|
12
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
12
13
|
|
|
13
14
|
export class SandpackPanel extends IFramePanel {
|
|
14
15
|
constructor(options: {
|
|
@@ -17,17 +18,44 @@ export class SandpackPanel extends IFramePanel {
|
|
|
17
18
|
}) {
|
|
18
19
|
super();
|
|
19
20
|
this._contentsManager = options.contentsManager;
|
|
21
|
+
const { context } = options;
|
|
20
22
|
options.context.ready.then(async () => {
|
|
21
|
-
|
|
23
|
+
const jpackModel =
|
|
24
|
+
context.model.toJSON() as any as IJupyterPackFileFormat;
|
|
25
|
+
await this.init(context.localPath, jpackModel);
|
|
22
26
|
});
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
dispose(): void {
|
|
30
|
+
this._fileModel?.fileChanged.disconnect(this._onFileChanged);
|
|
31
|
+
this._fileModel?.dispose();
|
|
32
|
+
this._spClient?.destroy();
|
|
33
|
+
super.dispose();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get isReady() {
|
|
37
|
+
return this._isReady.promise;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get autoreload(): boolean {
|
|
41
|
+
return this._autoreload;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
set autoreload(val: boolean) {
|
|
45
|
+
this._autoreload = val;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async init(localPath: string, jpackModel: IJupyterPackFileFormat) {
|
|
49
|
+
if (jpackModel?.metadata?.autoreload === true) {
|
|
50
|
+
this._autoreload = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
const currentDir = localPath.split('/').slice(0, -1).join('/');
|
|
27
54
|
const filesModel = new SandpackFilesModel({
|
|
28
55
|
contentsManager: this._contentsManager,
|
|
29
56
|
path: currentDir
|
|
30
57
|
});
|
|
58
|
+
this._fileModel = filesModel;
|
|
31
59
|
const allFiles = await filesModel.getAllFiles();
|
|
32
60
|
|
|
33
61
|
const options: ClientOptions = {
|
|
@@ -43,6 +71,7 @@ export class SandpackPanel extends IFramePanel {
|
|
|
43
71
|
options
|
|
44
72
|
);
|
|
45
73
|
await this.connectSignals(filesModel, this._spClient);
|
|
74
|
+
this._isReady.resolve();
|
|
46
75
|
}
|
|
47
76
|
|
|
48
77
|
async connectSignals(
|
|
@@ -66,11 +95,20 @@ export class SandpackPanel extends IFramePanel {
|
|
|
66
95
|
});
|
|
67
96
|
}
|
|
68
97
|
|
|
98
|
+
async reload(): Promise<void> {
|
|
99
|
+
if (this._spClient && this._fileModel) {
|
|
100
|
+
const allFiles = await this._fileModel.getAllFiles();
|
|
101
|
+
this._spClient.updateSandbox({
|
|
102
|
+
files: allFiles
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
69
107
|
private _onFileChanged(
|
|
70
108
|
sender: SandpackFilesModel,
|
|
71
109
|
{ allFiles }: { allFiles: IDict<{ code: string }> }
|
|
72
110
|
) {
|
|
73
|
-
if (this._spClient) {
|
|
111
|
+
if (this._autoreload && this._spClient) {
|
|
74
112
|
this._spClient.updateSandbox({
|
|
75
113
|
files: allFiles
|
|
76
114
|
});
|
|
@@ -79,4 +117,7 @@ export class SandpackPanel extends IFramePanel {
|
|
|
79
117
|
|
|
80
118
|
private _spClient?: SandpackClient;
|
|
81
119
|
private _contentsManager: Contents.IManager;
|
|
120
|
+
private _fileModel: SandpackFilesModel | undefined;
|
|
121
|
+
private _autoreload = false;
|
|
122
|
+
private _isReady = new PromiseDelegate<void>();
|
|
82
123
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { arrayBufferToBase64 } from '../tools';
|
|
1
|
+
import { arrayBufferToBase64, stringToBase64 } from '../tools';
|
|
2
2
|
import {
|
|
3
|
+
IBasePythonServer,
|
|
3
4
|
IBroadcastMessage,
|
|
4
5
|
IConnectionManager,
|
|
5
|
-
IDict
|
|
6
|
-
IKernelExecutor
|
|
6
|
+
IDict
|
|
7
7
|
} from '../type';
|
|
8
8
|
import { UUID } from '@lumino/coreutils';
|
|
9
9
|
|
|
@@ -16,20 +16,19 @@ import { UUID } from '@lumino/coreutils';
|
|
|
16
16
|
* It's running on the main thread
|
|
17
17
|
*/
|
|
18
18
|
export class ConnectionManager implements IConnectionManager {
|
|
19
|
-
constructor(public instanceId: string) {
|
|
20
|
-
this._wsBroadcastChannel = new BroadcastChannel(
|
|
21
|
-
`/jupyterpack/ws/${instanceId}`
|
|
22
|
-
);
|
|
23
|
-
this._initWsChannel();
|
|
24
|
-
}
|
|
19
|
+
constructor(public instanceId: string) {}
|
|
25
20
|
|
|
26
21
|
async registerConnection(
|
|
27
|
-
|
|
22
|
+
pythonServer: IBasePythonServer
|
|
28
23
|
): Promise<{ instanceId: string; kernelClientId: string }> {
|
|
29
24
|
const uuid = UUID.uuid4();
|
|
30
25
|
|
|
31
|
-
this.
|
|
32
|
-
|
|
26
|
+
this._pythonServers.set(uuid, pythonServer);
|
|
27
|
+
const wsbc = new BroadcastChannel(
|
|
28
|
+
`/jupyterpack/ws/${this.instanceId}/${uuid}`
|
|
29
|
+
);
|
|
30
|
+
this._initWsChannel(wsbc);
|
|
31
|
+
this._wsBroadcastChannelMap.set(`${this.instanceId}/${uuid}`, wsbc);
|
|
33
32
|
return { instanceId: this.instanceId, kernelClientId: uuid };
|
|
34
33
|
}
|
|
35
34
|
|
|
@@ -43,7 +42,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
43
42
|
}): Promise<IDict | null> {
|
|
44
43
|
const { urlPath, kernelClientId, method, params, requestBody, headers } =
|
|
45
44
|
options;
|
|
46
|
-
const executor = this.
|
|
45
|
+
const executor = this._pythonServers.get(kernelClientId);
|
|
47
46
|
if (!executor) {
|
|
48
47
|
return null;
|
|
49
48
|
}
|
|
@@ -57,8 +56,8 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
57
56
|
});
|
|
58
57
|
return response;
|
|
59
58
|
}
|
|
60
|
-
private _initWsChannel() {
|
|
61
|
-
|
|
59
|
+
private _initWsChannel(broadcastChannel: BroadcastChannel) {
|
|
60
|
+
broadcastChannel.onmessage = event => {
|
|
62
61
|
const rawData = event.data;
|
|
63
62
|
let data: IBroadcastMessage;
|
|
64
63
|
if (typeof rawData === 'string') {
|
|
@@ -68,13 +67,13 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
const { action, dest, wsUrl, payload } = data;
|
|
71
|
-
const executor = this.
|
|
70
|
+
const executor = this._pythonServers.get(dest);
|
|
72
71
|
if (!executor) {
|
|
73
72
|
console.error(
|
|
74
73
|
'Missing kernel handle for message',
|
|
75
74
|
data,
|
|
76
75
|
dest,
|
|
77
|
-
this.
|
|
76
|
+
this._pythonServers
|
|
78
77
|
);
|
|
79
78
|
return;
|
|
80
79
|
}
|
|
@@ -89,6 +88,14 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
89
88
|
});
|
|
90
89
|
break;
|
|
91
90
|
}
|
|
91
|
+
case 'close': {
|
|
92
|
+
executor.closeWebsocket({
|
|
93
|
+
instanceId: this.instanceId,
|
|
94
|
+
kernelId: dest,
|
|
95
|
+
wsUrl
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
92
99
|
case 'send': {
|
|
93
100
|
let serializedData: string;
|
|
94
101
|
let isBinary: boolean;
|
|
@@ -97,7 +104,8 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
97
104
|
serializedData = arrayBufferToBase64(payload as any);
|
|
98
105
|
isBinary = true;
|
|
99
106
|
} else if (typeof payload === 'string') {
|
|
100
|
-
|
|
107
|
+
// convert string to base64 string to avoid encoding problem
|
|
108
|
+
serializedData = stringToBase64(payload);
|
|
101
109
|
isBinary = false;
|
|
102
110
|
} else {
|
|
103
111
|
console.error('Unknown message type', payload);
|
|
@@ -116,6 +124,6 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
116
124
|
}
|
|
117
125
|
};
|
|
118
126
|
}
|
|
119
|
-
private
|
|
120
|
-
private
|
|
127
|
+
private _pythonServers = new Map<string, IBasePythonServer>();
|
|
128
|
+
private _wsBroadcastChannelMap: Map<string, BroadcastChannel> = new Map();
|
|
121
129
|
}
|
package/src/token.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Token } from '@lumino/coreutils';
|
|
2
|
-
import { IConnectionManager } from './type';
|
|
2
|
+
import { IConnectionManager, IJupyterpackDocTracker } from './type';
|
|
3
3
|
|
|
4
4
|
export const IConnectionManagerToken = new Token<IConnectionManager>(
|
|
5
5
|
'jupyterpack:connection-manager'
|
|
6
6
|
);
|
|
7
|
+
|
|
8
|
+
export const IJupyterpackDocTrackerToken = new Token<IJupyterpackDocTracker>(
|
|
9
|
+
'jupyterpack:dock-tracker'
|
|
10
|
+
);
|
package/src/tools.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
import logoStr from '../style/icons/box.svg';
|
|
2
|
+
import autoReloadStr from '../style/icons/autoreload.svg';
|
|
3
|
+
import linkStr from '../style/icons/externallink.svg';
|
|
4
|
+
import { LabIcon } from '@jupyterlab/ui-components';
|
|
5
|
+
|
|
6
|
+
export const IS_LITE = !!document.getElementById('jupyter-lite-main');
|
|
7
|
+
|
|
8
|
+
export const logoIcon = new LabIcon({
|
|
9
|
+
name: 'jupyterpack:logo',
|
|
10
|
+
svgstr: logoStr
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const autoReloadIcon = new LabIcon({
|
|
14
|
+
name: 'jupyterpack:autoReload',
|
|
15
|
+
svgstr: autoReloadStr
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const linkIcon = new LabIcon({
|
|
19
|
+
name: 'jupyterpack:externalLink',
|
|
20
|
+
svgstr: linkStr
|
|
21
|
+
});
|
|
22
|
+
|
|
1
23
|
export function removePrefix(path: string, prefix: string): string {
|
|
2
24
|
if (path.startsWith(prefix)) {
|
|
3
25
|
return path.slice(prefix.length);
|
|
@@ -31,6 +53,15 @@ export function base64ToString(base64: string): string {
|
|
|
31
53
|
return new TextDecoder('utf-8').decode(bytes);
|
|
32
54
|
}
|
|
33
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
|
+
|
|
34
65
|
export function stringOrNone(content?: string) {
|
|
35
66
|
return content ? `"${content}"` : 'None';
|
|
36
67
|
}
|
package/src/type.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
1
2
|
import { KernelMessage } from '@jupyterlab/services';
|
|
2
3
|
import { IDisposable } from '@lumino/disposable';
|
|
4
|
+
import { IWidgetTracker } from '@jupyterlab/apputils';
|
|
5
|
+
import { ISignal } from '@lumino/signaling';
|
|
3
6
|
|
|
4
7
|
export interface IDict<T = any> {
|
|
5
8
|
[key: string]: T;
|
|
@@ -23,13 +26,17 @@ export enum JupyterPackFramework {
|
|
|
23
26
|
REACT = 'react',
|
|
24
27
|
DASH = 'dash',
|
|
25
28
|
STREAMLIT = 'streamlit',
|
|
26
|
-
TORNADO = 'tornado'
|
|
29
|
+
TORNADO = 'tornado',
|
|
30
|
+
SHINY = 'shiny',
|
|
31
|
+
STARLETTE = 'starlette'
|
|
27
32
|
}
|
|
28
33
|
export interface IJupyterPackFileFormat {
|
|
29
34
|
entry: string;
|
|
30
35
|
framework: JupyterPackFramework;
|
|
31
36
|
name?: string;
|
|
32
|
-
metadata?:
|
|
37
|
+
metadata?: {
|
|
38
|
+
autoreload?: boolean;
|
|
39
|
+
};
|
|
33
40
|
rootUrl?: string;
|
|
34
41
|
}
|
|
35
42
|
|
|
@@ -44,7 +51,15 @@ export interface IKernelExecutorParams {
|
|
|
44
51
|
params?: string;
|
|
45
52
|
requestBody?: ArrayBuffer;
|
|
46
53
|
}
|
|
54
|
+
|
|
47
55
|
export interface IKernelExecutor extends IDisposable {
|
|
56
|
+
executeCode(
|
|
57
|
+
code: KernelMessage.IExecuteRequestMsg['content'],
|
|
58
|
+
waitForResult?: boolean
|
|
59
|
+
): Promise<string | null>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface IBasePythonServer extends IDisposable {
|
|
48
63
|
getResponse(options: IKernelExecutorParams): Promise<IDict>;
|
|
49
64
|
openWebsocket(options: {
|
|
50
65
|
instanceId: string;
|
|
@@ -52,16 +67,17 @@ export interface IKernelExecutor extends IDisposable {
|
|
|
52
67
|
wsUrl: string;
|
|
53
68
|
protocol?: string;
|
|
54
69
|
}): Promise<void>;
|
|
70
|
+
closeWebsocket(options: {
|
|
71
|
+
instanceId: string;
|
|
72
|
+
kernelId: string;
|
|
73
|
+
wsUrl: string;
|
|
74
|
+
}): Promise<void>;
|
|
55
75
|
sendWebsocketMessage(options: {
|
|
56
76
|
instanceId: string;
|
|
57
77
|
kernelId: string;
|
|
58
78
|
wsUrl: string;
|
|
59
79
|
message: string;
|
|
60
80
|
}): Promise<void>;
|
|
61
|
-
executeCode(
|
|
62
|
-
code: KernelMessage.IExecuteRequestMsg['content'],
|
|
63
|
-
waitForResult?: boolean
|
|
64
|
-
): Promise<string | null>;
|
|
65
81
|
init(options: {
|
|
66
82
|
entryPath?: string;
|
|
67
83
|
initCode?: string;
|
|
@@ -69,6 +85,10 @@ export interface IKernelExecutor extends IDisposable {
|
|
|
69
85
|
kernelClientId: string;
|
|
70
86
|
}): Promise<void>;
|
|
71
87
|
disposePythonServer(): Promise<void>;
|
|
88
|
+
reloadPythonServer(options: {
|
|
89
|
+
entryPath?: string;
|
|
90
|
+
initCode?: string;
|
|
91
|
+
}): Promise<void>;
|
|
72
92
|
getResponseFunctionFactory(options: {
|
|
73
93
|
urlPath: string;
|
|
74
94
|
method: string;
|
|
@@ -80,9 +100,28 @@ export interface IKernelExecutor extends IDisposable {
|
|
|
80
100
|
|
|
81
101
|
export interface IConnectionManager {
|
|
82
102
|
registerConnection(
|
|
83
|
-
kernelExecutor:
|
|
103
|
+
kernelExecutor: IBasePythonServer
|
|
84
104
|
): Promise<{ instanceId: string; kernelClientId: string }>;
|
|
85
105
|
generateResponse(
|
|
86
106
|
option: { kernelClientId: string } & IKernelExecutorParams
|
|
87
107
|
): Promise<IDict | null>;
|
|
88
108
|
}
|
|
109
|
+
|
|
110
|
+
export type IJupyterpackDocTracker = IWidgetTracker<DocumentWidget>;
|
|
111
|
+
|
|
112
|
+
export interface IPythonWidgetModel extends IDisposable {
|
|
113
|
+
connectionManager: IConnectionManager;
|
|
114
|
+
serverReloaded: ISignal<IPythonWidgetModel, void>;
|
|
115
|
+
kernelStatusChanged: ISignal<IPythonWidgetModel, 'started' | 'stopped'>;
|
|
116
|
+
reload(): Promise<void>;
|
|
117
|
+
initialize(): Promise<
|
|
118
|
+
| {
|
|
119
|
+
success: true;
|
|
120
|
+
instanceId: string;
|
|
121
|
+
kernelClientId: string;
|
|
122
|
+
rootUrl: string;
|
|
123
|
+
framework: JupyterPackFramework;
|
|
124
|
+
}
|
|
125
|
+
| { success: false; error: string }
|
|
126
|
+
>;
|
|
127
|
+
}
|
|
@@ -59,7 +59,9 @@
|
|
|
59
59
|
}
|
|
60
60
|
return data;
|
|
61
61
|
};
|
|
62
|
-
const bcWsChannel = new BroadcastChannel(
|
|
62
|
+
const bcWsChannel = new BroadcastChannel(
|
|
63
|
+
`/jupyterpack/ws/${instanceId}/${kernelClientId}`
|
|
64
|
+
);
|
|
63
65
|
|
|
64
66
|
class BroadcastChannelWebSocket implements WebSocket {
|
|
65
67
|
constructor(url: string | URL, protocols?: string | string[]) {
|
|
@@ -109,6 +111,10 @@
|
|
|
109
111
|
cb();
|
|
110
112
|
}
|
|
111
113
|
this._eventHandlers['close'] = [];
|
|
114
|
+
sendTypedMessage({
|
|
115
|
+
action: 'close',
|
|
116
|
+
wsUrl: this.url
|
|
117
|
+
});
|
|
112
118
|
bcWsChannel.removeEventListener('message', this._bcMessageHandler);
|
|
113
119
|
|
|
114
120
|
this.readyState = this.CLOSED;
|
|
@@ -168,10 +174,12 @@
|
|
|
168
174
|
} else {
|
|
169
175
|
data = rawData;
|
|
170
176
|
}
|
|
177
|
+
|
|
171
178
|
const { action, dest, wsUrl, payload } = data;
|
|
172
179
|
if (dest !== kernelClientId || wsUrl !== this.url) {
|
|
173
180
|
return;
|
|
174
181
|
}
|
|
182
|
+
|
|
175
183
|
switch (action) {
|
|
176
184
|
case 'connected': {
|
|
177
185
|
this.readyState = this.OPEN;
|
package/style/base.css
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
|
|
3
|
+
<svg height="800px" width="800px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<g>
|
|
5
|
+
<path d="M403.925,108.102c-27.595-27.595-62.899-47.558-102.459-56.29L304.182,0L201.946,53.867l-27.306,14.454
|
|
6
|
+
l-5.066,2.654l8.076,4.331l38.16,20.542l81.029,43.602l2.277-42.859c28.265,7.546,53.438,22.53,73.623,42.638
|
|
7
|
+
c29.94,29.939,48.358,71.119,48.358,116.776c0,23.407-4.843,45.58-13.575,65.687l40.37,17.532
|
|
8
|
+
c11.076-25.463,17.242-53.637,17.242-83.219C465.212,198.306,441.727,145.904,403.925,108.102z"
|
|
9
|
+
fill="var(--jp-inverse-layout-color3)" />
|
|
10
|
+
<path d="M296.256,416.151l-81.101-43.612l-2.272,42.869c-28.26-7.555-53.51-22.53-73.618-42.636
|
|
11
|
+
c-29.945-29.95-48.364-71.12-48.364-116.767c0-23.427,4.844-45.522,13.576-65.697l-40.37-17.531
|
|
12
|
+
c-11.076,25.53-17.242,53.723-17.242,83.228c0,57.679,23.407,110.157,61.21,147.893c27.595,27.594,62.899,47.548,102.453,56.202
|
|
13
|
+
l-2.716,51.9l102.169-53.878l27.455-14.454l4.988-2.643l-7.999-4.332L296.256,416.151z"
|
|
14
|
+
fill="var(--jp-inverse-layout-color3)" />
|
|
15
|
+
</g>
|
|
16
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path
|
|
4
|
+
d="M17.5777 4.43152L15.5777 3.38197C13.8221 2.46066 12.9443 2 12 2C11.0557 2 10.1779 2.46066 8.42229 3.38197L6.42229 4.43152C4.64855 5.36234 3.6059 5.9095 2.95969 6.64132L12 11.1615L21.0403 6.64132C20.3941 5.9095 19.3515 5.36234 17.5777 4.43152Z"
|
|
5
|
+
fill="var(--jp-inverse-layout-color3)" />
|
|
6
|
+
<path
|
|
7
|
+
d="M21.7484 7.96435L12.75 12.4635V21.904C13.4679 21.7252 14.2848 21.2965 15.5777 20.618L17.5777 19.5685C19.7294 18.4393 20.8052 17.8748 21.4026 16.8603C22 15.8458 22 14.5833 22 12.0585V11.9415C22 10.0489 22 8.86558 21.7484 7.96435Z"
|
|
8
|
+
fill="var(--jp-inverse-layout-color3)" />
|
|
9
|
+
<path
|
|
10
|
+
d="M11.25 21.904V12.4635L2.25164 7.96434C2 8.86557 2 10.0489 2 11.9415V12.0585C2 14.5833 2 15.8458 2.5974 16.8603C3.19479 17.8748 4.27063 18.4393 6.42229 19.5685L8.42229 20.618C9.71524 21.2965 10.5321 21.7252 11.25 21.904Z"
|
|
11
|
+
fill="var(--jp-inverse-layout-color3)" />
|
|
12
|
+
</svg>
|