@jupyterlite/xeus 4.0.0-a0 → 4.0.0-a10

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/lib/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- export * from './coincident.worker';
2
- export * from './comlink.worker';
3
1
  export * from './interfaces';
4
2
  export * from './web_worker_kernel';
3
+ export * from './worker.base';
5
4
  export * from './worker';
package/lib/index.js CHANGED
@@ -1,5 +1,4 @@
1
- export * from './coincident.worker';
2
- export * from './comlink.worker';
3
1
  export * from './interfaces';
4
2
  export * from './web_worker_kernel';
3
+ export * from './worker.base';
5
4
  export * from './worker';
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Definitions for the Xeus kernel.
3
3
  */
4
+ import type { KernelMessage } from '@jupyterlab/services';
4
5
  import { TDriveMethod, TDriveRequest, TDriveResponse } from '@jupyterlite/contents';
5
6
  import { IWorkerKernel } from '@jupyterlite/kernel';
6
7
  /**
@@ -21,15 +22,17 @@ export interface IXeusWorkerKernel extends IWorkerKernel {
21
22
  * @param msg
22
23
  */
23
24
  processMessage(msg: any): void;
25
+ /**
26
+ * Process stdin request, blocking until the reply is received.
27
+ * This is sync for the web worker, async for the UI thread.
28
+ * @param inputRequest
29
+ */
30
+ processStdinRequest(inputRequest: KernelMessage.IInputRequestMsg): KernelMessage.IInputReplyMsg;
24
31
  /**
25
32
  * Process worker message
26
33
  * @param msg
27
34
  */
28
35
  processWorkerMessage(msg: any): void;
29
- /**
30
- * Register a callback for handling messages from the worker.
31
- */
32
- registerCallback(callback: (msg: any) => void): void;
33
36
  /**
34
37
  * Whether the kernel is ready.
35
38
  * @returns a promise that resolves when the kernel is ready.
@@ -40,8 +43,9 @@ export interface IXeusWorkerKernel extends IWorkerKernel {
40
43
  * @param driveName The name of the drive
41
44
  * @param mountpoint The mountpoint of the drive
42
45
  * @param baseUrl The base URL of the server
46
+ * @param browsingContextId The current page id
43
47
  */
44
- mount(driveName: string, mountpoint: string, baseUrl: string): Promise<void>;
48
+ mount(driveName: string, mountpoint: string, baseUrl: string, browsingContextId: string): Promise<void>;
45
49
  /**
46
50
  * Change the current working directory
47
51
  * @param path The path to change to
@@ -62,8 +66,10 @@ export declare namespace IXeusWorkerKernel {
62
66
  */
63
67
  interface IOptions extends IWorkerKernel.IOptions {
64
68
  baseUrl: string;
69
+ kernelId: string;
65
70
  kernelSpec: any;
66
71
  mountDrive: boolean;
67
72
  empackEnvMetaLink?: string;
73
+ browsingContextId: string;
68
74
  }
69
75
  }
@@ -36,12 +36,17 @@ export declare class WebWorkerKernel implements IKernel {
36
36
  * Get the kernel location
37
37
  */
38
38
  get location(): string;
39
+ /**
40
+ * Send kernel message to the client
41
+ */
42
+ protected get sendMessage(): IKernel.SendMessage;
39
43
  /**
40
44
  * Process a message coming from the coincident web worker.
41
45
  *
42
46
  * @param msg The worker message to process.
43
47
  */
44
48
  private _processCoincidentWorkerMessage;
49
+ private _assignSession;
45
50
  /**
46
51
  * Process a message coming from the comlink web worker.
47
52
  *
@@ -85,6 +90,7 @@ export declare class WebWorkerKernel implements IKernel {
85
90
  private _worker;
86
91
  private _sendMessage;
87
92
  private _executeDelegate;
93
+ private _inputDelegate;
88
94
  private _parentHeader;
89
95
  private _parent;
90
96
  private _ready;
@@ -102,5 +108,6 @@ export declare namespace WebWorkerKernel {
102
108
  mountDrive: boolean;
103
109
  kernelSpec: any;
104
110
  empackEnvMetaLink?: string | undefined;
111
+ browsingContextId: string;
105
112
  }
106
113
  }
@@ -6,6 +6,7 @@ import { wrap } from 'comlink';
6
6
  import { Signal } from '@lumino/signaling';
7
7
  import { PromiseDelegate } from '@lumino/coreutils';
8
8
  import { PageConfig } from '@jupyterlab/coreutils';
9
+ import { KernelMessage } from '@jupyterlab/services';
9
10
  import { DriveContentsProcessor } from '@jupyterlite/contents';
10
11
  export class WebWorkerKernel {
11
12
  /**
@@ -18,6 +19,7 @@ export class WebWorkerKernel {
18
19
  this._isDisposed = false;
19
20
  this._disposed = new Signal(this);
20
21
  this._executeDelegate = new PromiseDelegate();
22
+ this._inputDelegate = new PromiseDelegate();
21
23
  this._parentHeader = undefined;
22
24
  this._parent = undefined;
23
25
  this._ready = new PromiseDelegate();
@@ -74,6 +76,14 @@ export class WebWorkerKernel {
74
76
  }
75
77
  return await this._contentsProcessor.processDriveRequest(data);
76
78
  };
79
+ // Stdin request is synchronous from the web worker's point of view, blocking
80
+ // until the reply is received. From the UI thread's point of view it is async.
81
+ remote.processStdinRequest = async (inputRequest) => {
82
+ this._processCoincidentWorkerMessage({ data: inputRequest });
83
+ this._inputDelegate =
84
+ new PromiseDelegate();
85
+ return await this._inputDelegate.promise;
86
+ };
77
87
  }
78
88
  else {
79
89
  this._worker.onmessage = e => {
@@ -83,10 +93,12 @@ export class WebWorkerKernel {
83
93
  }
84
94
  remote
85
95
  .initialize({
96
+ kernelId: this.id,
86
97
  kernelSpec: this._kernelSpec,
87
98
  baseUrl: PageConfig.getBaseUrl(),
88
99
  mountDrive: options.mountDrive,
89
- empackEnvMetaLink: this._empackEnvMetaLink
100
+ empackEnvMetaLink: this._empackEnvMetaLink,
101
+ browsingContextId: options.browsingContextId
90
102
  })
91
103
  .then(this._ready.resolve.bind(this._ready));
92
104
  return remote;
@@ -97,11 +109,12 @@ export class WebWorkerKernel {
97
109
  await this._sendMessageToWorker(msg);
98
110
  }
99
111
  async _sendMessageToWorker(msg) {
100
- if (msg.header.msg_type !== 'input_reply') {
101
- this._executeDelegate = new PromiseDelegate();
112
+ if (msg.header.msg_type === 'input_reply') {
113
+ this._inputDelegate.resolve(msg);
102
114
  }
103
- await this._remoteKernel.processMessage({ msg, parent: this.parent });
104
- if (msg.header.msg_type !== 'input_reply') {
115
+ else {
116
+ this._executeDelegate = new PromiseDelegate();
117
+ await this._remoteKernel.processMessage({ msg, parent: this.parent });
105
118
  return await this._executeDelegate.promise;
106
119
  }
107
120
  }
@@ -123,6 +136,12 @@ export class WebWorkerKernel {
123
136
  get location() {
124
137
  return this._location;
125
138
  }
139
+ /**
140
+ * Send kernel message to the client
141
+ */
142
+ get sendMessage() {
143
+ return this._sendMessage;
144
+ }
126
145
  /**
127
146
  * Process a message coming from the coincident web worker.
128
147
  *
@@ -142,19 +161,60 @@ export class WebWorkerKernel {
142
161
  this._executeDelegate.resolve();
143
162
  }
144
163
  }
164
+ _assignSession(msg) {
165
+ var _a, _b, _c, _d;
166
+ msg.header.session = (_b = (_a = this._parentHeader) === null || _a === void 0 ? void 0 : _a.session) !== null && _b !== void 0 ? _b : '';
167
+ msg.session = (_d = (_c = this._parentHeader) === null || _c === void 0 ? void 0 : _c.session) !== null && _d !== void 0 ? _d : '';
168
+ return msg;
169
+ }
145
170
  /**
146
171
  * Process a message coming from the comlink web worker.
147
172
  *
148
173
  * @param msg The worker message to process.
149
174
  */
150
175
  _processComlinkWorkerMessage(msg) {
151
- var _a, _b, _c, _d;
176
+ var _a, _b;
152
177
  if (!msg.header) {
153
- return;
178
+ if (msg === null || msg === void 0 ? void 0 : msg._stream) {
179
+ const parentHeaderValue = this._parentHeader;
180
+ const { name, text } = msg._stream;
181
+ if (name === 'stderr') {
182
+ const errorMessage = KernelMessage.createMessage({
183
+ msgType: 'execute_reply',
184
+ channel: 'shell',
185
+ parentHeader: parentHeaderValue,
186
+ session: (_a = parentHeaderValue === null || parentHeaderValue === void 0 ? void 0 : parentHeaderValue.session) !== null && _a !== void 0 ? _a : '',
187
+ content: {
188
+ execution_count: msg._stream.executionCount,
189
+ status: 'error',
190
+ ename: msg._stream.ename,
191
+ evalue: msg._stream.evalue,
192
+ traceback: msg._stream.traceback.join('')
193
+ }
194
+ });
195
+ msg = this._assignSession(errorMessage);
196
+ this._sendMessage(msg);
197
+ }
198
+ const message = KernelMessage.createMessage({
199
+ channel: 'iopub',
200
+ msgType: 'stream',
201
+ session: (_b = parentHeaderValue === null || parentHeaderValue === void 0 ? void 0 : parentHeaderValue.session) !== null && _b !== void 0 ? _b : '',
202
+ parentHeader: parentHeaderValue,
203
+ content: {
204
+ name,
205
+ text
206
+ }
207
+ });
208
+ msg = this._assignSession(message);
209
+ this._sendMessage(msg);
210
+ }
211
+ else {
212
+ return;
213
+ }
214
+ }
215
+ else {
216
+ this._sendMessage(this._assignSession(msg));
154
217
  }
155
- msg.header.session = (_b = (_a = this._parentHeader) === null || _a === void 0 ? void 0 : _a.session) !== null && _b !== void 0 ? _b : '';
156
- msg.session = (_d = (_c = this._parentHeader) === null || _c === void 0 ? void 0 : _c.session) !== null && _d !== void 0 ? _d : '';
157
- this._sendMessage(msg);
158
218
  // resolve promise
159
219
  if (msg.header.msg_type === 'status' &&
160
220
  msg.content.execution_state === 'idle') {
@@ -217,12 +277,12 @@ export class WebWorkerKernel {
217
277
  localPath = options.location;
218
278
  }
219
279
  await this._remoteKernel.ready();
220
- await this._remoteKernel.mount(driveName, '/drive', PageConfig.getBaseUrl());
280
+ await this._remoteKernel.mount(driveName, '/drive', PageConfig.getBaseUrl(), options.browsingContextId);
221
281
  if (await this._remoteKernel.isDir('/files')) {
222
282
  await this._remoteKernel.cd('/files');
223
283
  }
224
284
  else {
225
- await this._remoteKernel.cd(localPath);
285
+ await this._remoteKernel.cd(`/drive/${localPath}`);
226
286
  }
227
287
  }
228
288
  }
@@ -0,0 +1,76 @@
1
+ import { IXeusWorkerKernel } from './interfaces';
2
+ import { ILogger } from '@emscripten-forge/mambajs';
3
+ export declare class XeusWorkerLoggerBase implements ILogger {
4
+ constructor(kernelId: string);
5
+ log(...msg: any[]): void;
6
+ warn(...msg: any[]): void;
7
+ error(...msg: any[]): void;
8
+ private _id;
9
+ private _channel;
10
+ executionCount: number;
11
+ }
12
+ /**
13
+ * The base class for the worker kernel
14
+ *
15
+ * Meant to be extended in order to load kernels from other sources, implement custom magics etc.
16
+ */
17
+ export declare abstract class XeusRemoteKernelBase {
18
+ constructor(options?: XeusRemoteKernelBase.IOptions);
19
+ ready(): Promise<void>;
20
+ cd(path: string): Promise<void>;
21
+ isDir(path: string): Promise<boolean>;
22
+ processMessage(event: any): Promise<void>;
23
+ protected get Module(): any;
24
+ protected set Module(value: any);
25
+ protected get logger(): XeusWorkerLoggerBase;
26
+ initialize(options: IXeusWorkerKernel.IOptions): Promise<void>;
27
+ protected initializeLogger(options: IXeusWorkerKernel.IOptions): XeusWorkerLoggerBase;
28
+ /**
29
+ * Initialize the emscripten Module
30
+ * @param options
31
+ */
32
+ protected abstract initializeModule(options: IXeusWorkerKernel.IOptions): any;
33
+ /**
34
+ * Initialize the this.Module.FS as needed
35
+ * @param options
36
+ */
37
+ protected abstract initializeFileSystem(options: IXeusWorkerKernel.IOptions): Promise<any>;
38
+ /**
39
+ * Initialize the interpreter if needed
40
+ * @param options
41
+ */
42
+ protected abstract initializeInterpreter(options: IXeusWorkerKernel.IOptions): Promise<any>;
43
+ /**
44
+ * Add get_stdin function to globalThis that takes an input_request message, blocks
45
+ * until the corresponding input_reply is received and returns the input_reply message.
46
+ * If an error occurs return an object of the form { error: "Error explanation" }
47
+ * This function is called by xeus-lite's get_stdin.
48
+ */
49
+ protected abstract initializeStdin(baseUrl: string, browsingContextId: string): void;
50
+ /**
51
+ * Setup custom Emscripten FileSystem
52
+ */
53
+ abstract mount(driveName: string, mountpoint: string, baseUrl: string, browsingContextId: string): Promise<void>;
54
+ /**
55
+ * Implements dynamic installation of packages
56
+ */
57
+ protected abstract install(channels: string[], specs: string[], pipSpecs: string[]): Promise<void>;
58
+ /**
59
+ * Implements dynamic installation of packages
60
+ */
61
+ protected abstract listInstalledPackages(): Promise<void>;
62
+ /**
63
+ * Process magics prior to executing code
64
+ * @returns the runnable code without magics
65
+ */
66
+ protected processMagics(code: string): Promise<string>;
67
+ protected xkernel: any;
68
+ protected xserver: any;
69
+ protected setKernelReady: (value: void) => void;
70
+ private _ready;
71
+ private _logger;
72
+ }
73
+ export declare namespace XeusRemoteKernelBase {
74
+ interface IOptions {
75
+ }
76
+ }
@@ -0,0 +1,185 @@
1
+ // Copyright (c) Thorsten Beier
2
+ // Copyright (c) JupyterLite Contributors
3
+ // Distributed under the terms of the Modified BSD License.
4
+ import { waitRunDependencies, parse } from '@emscripten-forge/mambajs';
5
+ const STREAM = { log: 'stdout', warn: 'stdout', error: 'stderr' };
6
+ export class XeusWorkerLoggerBase {
7
+ constructor(kernelId) {
8
+ this.executionCount = 0;
9
+ this._id = kernelId;
10
+ this._channel = new BroadcastChannel('/xeus-kernel-logs-broadcast');
11
+ }
12
+ log(...msg) {
13
+ postMessage({
14
+ _stream: {
15
+ name: STREAM['log'],
16
+ text: msg.join(' ') + '\n'
17
+ }
18
+ });
19
+ this._channel.postMessage({
20
+ kernelId: this._id,
21
+ payload: { type: 'text', level: 'info', data: msg.join(' ') }
22
+ });
23
+ }
24
+ warn(...msg) {
25
+ postMessage({
26
+ _stream: {
27
+ name: STREAM['warn'],
28
+ text: msg.join(' ') + '\n'
29
+ }
30
+ });
31
+ this._channel.postMessage({
32
+ kernelId: this._id,
33
+ payload: { type: 'text', level: 'warning', data: msg.join(' ') }
34
+ });
35
+ }
36
+ error(...msg) {
37
+ postMessage({
38
+ _stream: {
39
+ name: STREAM['error'],
40
+ evalue: msg.join(''),
41
+ traceback: [],
42
+ executionCount: this.executionCount,
43
+ text: msg.join('')
44
+ }
45
+ });
46
+ this._channel.postMessage({
47
+ kernelId: this._id,
48
+ payload: { type: 'text', level: 'critical', data: msg.join(' ') }
49
+ });
50
+ }
51
+ }
52
+ /**
53
+ * The base class for the worker kernel
54
+ *
55
+ * Meant to be extended in order to load kernels from other sources, implement custom magics etc.
56
+ */
57
+ export class XeusRemoteKernelBase {
58
+ constructor(options = {}) {
59
+ this._ready = new Promise(resolve => {
60
+ this.setKernelReady = resolve;
61
+ });
62
+ }
63
+ async ready() {
64
+ return await this._ready;
65
+ }
66
+ async cd(path) {
67
+ if (!path || !this.Module.FS) {
68
+ return;
69
+ }
70
+ this.Module.FS.chdir(path);
71
+ }
72
+ async isDir(path) {
73
+ try {
74
+ const lookup = this.Module.FS.lookupPath(path);
75
+ return this.Module.FS.isDir(lookup.node.mode);
76
+ }
77
+ catch (e) {
78
+ return false;
79
+ }
80
+ }
81
+ async processMessage(event) {
82
+ const msg_type = event.msg.header.msg_type;
83
+ await globalThis.ready;
84
+ if (globalThis.toplevel_promise !== null &&
85
+ globalThis.toplevel_promise_py_proxy !== null) {
86
+ await globalThis.toplevel_promise;
87
+ globalThis.toplevel_promise_py_proxy.delete();
88
+ globalThis.toplevel_promise_py_proxy = null;
89
+ globalThis.toplevel_promise = null;
90
+ }
91
+ if (msg_type === 'input_reply') {
92
+ // Should never be called as input_reply messages are handled by get_stdin
93
+ // via SharedArrayBuffer or service worker.
94
+ }
95
+ else if (msg_type === 'execute_request') {
96
+ this._logger.executionCount += 1;
97
+ event.msg.content.code = await this.processMagics(event.msg.content.code);
98
+ this.xserver.notify_listener(event.msg);
99
+ }
100
+ else {
101
+ this.xserver.notify_listener(event.msg);
102
+ }
103
+ }
104
+ get Module() {
105
+ return globalThis.Module;
106
+ }
107
+ set Module(value) {
108
+ globalThis.Module = value;
109
+ }
110
+ get logger() {
111
+ return this._logger;
112
+ }
113
+ async initialize(options) {
114
+ const { baseUrl, browsingContextId, kernelSpec } = options;
115
+ this._logger = this.initializeLogger(options);
116
+ // when a toplevel cell uses an await, the cell is implicitly
117
+ // wrapped in a async function. Since the webloop - eventloop
118
+ // implementation does not support `eventloop.run_until_complete(f)`
119
+ // we need to convert the toplevel future in a javascript Promise
120
+ // this `toplevel` promise is then awaited before we
121
+ // execute the next cell. After the promise is awaited we need
122
+ // to do some cleanup and delete the python proxy
123
+ // (ie a js-wrapped python object) to avoid memory leaks
124
+ globalThis.toplevel_promise = null;
125
+ globalThis.toplevel_promise_py_proxy = null;
126
+ this.Module = await this.initializeModule(options);
127
+ this.Module = await createXeusModule(this.Module);
128
+ try {
129
+ await waitRunDependencies(this.Module);
130
+ await this.initializeFileSystem(options);
131
+ await this.initializeInterpreter(options);
132
+ this.initializeStdin(baseUrl, browsingContextId);
133
+ try {
134
+ this.xkernel = new this.Module.xkernel(kernelSpec.argv);
135
+ }
136
+ catch (e) {
137
+ this.xkernel = new this.Module.xkernel();
138
+ }
139
+ this.xserver = this.xkernel.get_server();
140
+ if (!this.xserver) {
141
+ this._logger.error('Failed to start kernel!');
142
+ }
143
+ this.xkernel.start();
144
+ }
145
+ catch (e) {
146
+ if (typeof e === 'number') {
147
+ const msg = this.Module.get_exception_message(e);
148
+ this._logger.error(msg);
149
+ throw new Error(msg);
150
+ }
151
+ else {
152
+ this._logger.error(e);
153
+ throw e;
154
+ }
155
+ }
156
+ this._logger.log('Kernel successfuly started!');
157
+ this.setKernelReady();
158
+ }
159
+ initializeLogger(options) {
160
+ return new XeusWorkerLoggerBase(options.kernelId);
161
+ }
162
+ /**
163
+ * Process magics prior to executing code
164
+ * @returns the runnable code without magics
165
+ */
166
+ async processMagics(code) {
167
+ const { commands, run } = parse(code);
168
+ for (const command of commands) {
169
+ switch (command.type) {
170
+ case 'install':
171
+ if (command.data) {
172
+ const { channels, specs, pipSpecs } = command.data;
173
+ await this.install(channels, specs, pipSpecs);
174
+ }
175
+ break;
176
+ case 'list':
177
+ await this.listInstalledPackages();
178
+ break;
179
+ default:
180
+ break;
181
+ }
182
+ }
183
+ return run;
184
+ }
185
+ }
package/lib/worker.d.ts CHANGED
@@ -1,17 +1,29 @@
1
1
  import { IXeusWorkerKernel } from './interfaces';
2
- export declare class XeusRemoteKernel {
3
- constructor(options?: XeusRemoteKernel.IOptions);
4
- ready(): Promise<void>;
5
- cd(path: string): Promise<void>;
6
- isDir(path: string): Promise<boolean>;
7
- processMessage(event: any): Promise<void>;
8
- initialize(options: IXeusWorkerKernel.IOptions): Promise<void>;
2
+ import { XeusRemoteKernelBase } from './worker.base';
3
+ /**
4
+ * A worker kernel that is backed by an empack environment
5
+ */
6
+ export declare abstract class EmpackedXeusRemoteKernel extends XeusRemoteKernelBase {
7
+ protected initializeModule(options: IXeusWorkerKernel.IOptions): Promise<any>;
8
+ protected initializeFileSystem(options: IXeusWorkerKernel.IOptions): Promise<any>;
9
9
  /**
10
- * Register the callback function to send messages from the worker back to the main thread.
11
- * @param callback the callback to register
10
+ * Initialize the interpreter if needed
11
+ * @param options
12
12
  */
13
- registerCallback(callback: (msg: any) => void): void;
14
- protected _sendWorkerMessage: (msg: any) => void;
13
+ protected initializeInterpreter(options: IXeusWorkerKernel.IOptions): Promise<void>;
14
+ protected install(channels: string[], specs: string[], pipSpecs: string[]): Promise<void>;
15
+ /**
16
+ * Implements dynamic installation of packages
17
+ */
18
+ protected listInstalledPackages(): Promise<void>;
19
+ private _reloadPackagesInFS;
20
+ private _pythonVersion;
21
+ private _prefix;
22
+ private _pkgRootUrl;
23
+ private _sharedLibs;
24
+ private _installedPackages;
25
+ private _paths;
26
+ private _untarjs;
15
27
  }
16
28
  export declare namespace XeusRemoteKernel {
17
29
  interface IOptions {