@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/worker.js CHANGED
@@ -2,28 +2,8 @@
2
2
  // Copyright (c) JupyterLite Contributors
3
3
  // Distributed under the terms of the Modified BSD License.
4
4
  import { URLExt } from '@jupyterlab/coreutils';
5
- import { bootstrapEmpackPackedEnvironment, bootstrapPython, getPythonVersion, loadShareLibs, waitRunDependencies } from '@emscripten-forge/mambajs';
6
- globalThis.Module = {};
7
- // when a toplevel cell uses an await, the cell is implicitly
8
- // wrapped in a async function. Since the webloop - eventloop
9
- // implementation does not support `eventloop.run_until_complete(f)`
10
- // we need to convert the toplevel future in a javascript Promise
11
- // this `toplevel` promise is then awaited before we
12
- // execute the next cell. After the promise is awaited we need
13
- // to do some cleanup and delete the python proxy
14
- // (ie a js-wrapped python object) to avoid memory leaks
15
- globalThis.toplevel_promise = null;
16
- globalThis.toplevel_promise_py_proxy = null;
17
- let resolveInputReply;
18
- let kernelReady;
19
- let rawXKernel;
20
- let rawXServer;
21
- async function get_stdin() {
22
- const replyPromise = new Promise(resolve => {
23
- resolveInputReply = resolve;
24
- });
25
- return replyPromise;
26
- }
5
+ import { bootstrapEmpackPackedEnvironment, bootstrapPython, getPythonVersion, loadShareLibs, solve, installPackagesToEmscriptenFS, removePackagesFromEmscriptenFS, showPackagesList } from '@emscripten-forge/mambajs';
6
+ import { XeusRemoteKernelBase } from './worker.base';
27
7
  async function fetchJson(url) {
28
8
  const response = await fetch(url);
29
9
  if (!response.ok) {
@@ -32,131 +12,190 @@ async function fetchJson(url) {
32
12
  const json = await response.json();
33
13
  return json;
34
14
  }
35
- self.get_stdin = get_stdin;
36
- globalThis.ready = new Promise(resolve => {
37
- kernelReady = resolve;
38
- });
39
- export class XeusRemoteKernel {
40
- constructor(options = {}) {
41
- this._sendWorkerMessage = () => { };
42
- }
43
- async ready() {
44
- return await globalThis.ready;
45
- }
46
- async cd(path) {
47
- if (!path || !globalThis.Module.FS) {
48
- return;
49
- }
50
- globalThis.Module.FS.chdir(path);
51
- }
52
- async isDir(path) {
53
- try {
54
- const lookup = globalThis.Module.FS.lookupPath(path);
55
- return globalThis.Module.FS.isDir(lookup.node.mode);
56
- }
57
- catch (e) {
58
- return false;
59
- }
15
+ /**
16
+ * A worker kernel that is backed by an empack environment
17
+ */
18
+ export class EmpackedXeusRemoteKernel extends XeusRemoteKernelBase {
19
+ constructor() {
20
+ super(...arguments);
21
+ this._prefix = '';
22
+ this._pkgRootUrl = '';
23
+ this._installedPackages = {};
24
+ this._paths = {};
60
25
  }
61
- async processMessage(event) {
62
- const msg_type = event.msg.header.msg_type;
63
- await globalThis.ready;
64
- if (globalThis.toplevel_promise !== null &&
65
- globalThis.toplevel_promise_py_proxy !== null) {
66
- await globalThis.toplevel_promise;
67
- globalThis.toplevel_promise_py_proxy.delete();
68
- globalThis.toplevel_promise_py_proxy = null;
69
- globalThis.toplevel_promise = null;
70
- }
71
- if (msg_type === 'input_reply') {
72
- resolveInputReply(event.msg);
73
- }
74
- else {
75
- rawXServer.notify_listener(event.msg);
76
- }
77
- }
78
- async initialize(options) {
79
- const { baseUrl, kernelSpec, empackEnvMetaLink } = options;
26
+ async initializeModule(options) {
27
+ const { baseUrl, kernelSpec } = options;
80
28
  // location of the kernel binary on the server
81
- const binary_js = URLExt.join(baseUrl, kernelSpec.argv[0]);
82
- const binary_wasm = binary_js.replace('.js', '.wasm');
83
- const binary_data = binary_js.replace('.js', '.data');
84
- const kernel_root_url = URLExt.join(baseUrl, 'xeus', 'kernels', kernelSpec.dir);
29
+ const binaryJS = URLExt.join(baseUrl, kernelSpec.argv[0]);
30
+ const binaryWASM = binaryJS.replace('.js', '.wasm');
31
+ const binaryDATA = binaryJS.replace('.js', '.data');
32
+ const kernelRootUrl = URLExt.join(baseUrl, 'xeus', kernelSpec.envName);
85
33
  const sharedLibs = kernelSpec.metadata && kernelSpec.metadata.shared
86
34
  ? kernelSpec.metadata.shared
87
35
  : {};
88
- importScripts(binary_js);
89
- globalThis.Module = await createXeusModule({
36
+ importScripts(binaryJS);
37
+ return {
90
38
  locateFile: (file) => {
91
39
  if (file in sharedLibs) {
92
- return URLExt.join(kernel_root_url, file);
40
+ return URLExt.join(kernelRootUrl, kernelSpec.name, file);
93
41
  }
94
42
  if (file.endsWith('.wasm')) {
95
- return binary_wasm;
43
+ return binaryWASM;
96
44
  }
97
45
  else if (file.endsWith('.data')) {
98
46
  // Handle the .data file if it exists
99
- return binary_data;
47
+ return binaryDATA;
100
48
  }
101
49
  return file;
102
50
  }
51
+ };
52
+ }
53
+ async initializeFileSystem(options) {
54
+ const { baseUrl, kernelSpec, empackEnvMetaLink } = options;
55
+ if (this.Module.FS === undefined ||
56
+ this.Module.loadDynamicLibrary === undefined) {
57
+ throw new Error('Cannot load kernel without a valid FS');
58
+ }
59
+ // location of the kernel binary on the server
60
+ const kernelRootUrl = URLExt.join(baseUrl, 'xeus', 'kernels', kernelSpec.dir);
61
+ const empackEnvMetaLocation = empackEnvMetaLink || kernelRootUrl;
62
+ const packagesJsonUrl = `${empackEnvMetaLocation}/empack_env_meta.json`;
63
+ this._pkgRootUrl = URLExt.join(baseUrl, `xeus/${kernelSpec.envName}/kernel_packages`);
64
+ const empackEnvMeta = (await fetchJson(packagesJsonUrl));
65
+ // Initialize installed packages from empack env meta
66
+ this._installedPackages = {};
67
+ empackEnvMeta.packages.map(pkg => {
68
+ this._installedPackages[pkg.filename] = {
69
+ name: pkg.name,
70
+ version: pkg.version,
71
+ repo_url: pkg.channel ? pkg.channel : '',
72
+ url: pkg.url ? pkg.url : '',
73
+ repo_name: pkg.channel ? pkg.channel : '',
74
+ build_string: pkg.build
75
+ };
103
76
  });
104
- try {
105
- await waitRunDependencies(globalThis.Module);
106
- if (globalThis.Module.FS !== undefined &&
107
- globalThis.Module.loadDynamicLibrary !== undefined) {
108
- const empackEnvMetaLocation = empackEnvMetaLink || kernel_root_url;
109
- const packagesJsonUrl = `${empackEnvMetaLocation}/empack_env_meta.json`;
110
- const pkgRootUrl = URLExt.join(baseUrl, `xeus/kernels/${kernelSpec.name}/kernel_packages`);
111
- const empackEnvMeta = (await fetchJson(packagesJsonUrl));
112
- const sharedLibs = await bootstrapEmpackPackedEnvironment({
113
- empackEnvMeta,
114
- pkgRootUrl,
115
- Module: globalThis.Module,
116
- verbose: false
117
- });
118
- // Bootstrap Python, if it's xeus-python
119
- if (kernelSpec.name === 'xpython') {
120
- const pythonVersion = getPythonVersion(empackEnvMeta.packages);
121
- if (!pythonVersion) {
122
- throw new Error('Failed to load Python!');
123
- }
124
- await bootstrapPython({
125
- prefix: empackEnvMeta.prefix,
126
- pythonVersion: pythonVersion,
127
- Module: globalThis.Module
128
- });
129
- }
130
- // Load shared libs
131
- await loadShareLibs({
132
- sharedLibs,
133
- prefix: empackEnvMeta.prefix,
134
- Module: globalThis.Module
135
- });
136
- }
137
- rawXKernel = new globalThis.Module.xkernel();
138
- rawXServer = rawXKernel.get_server();
139
- if (!rawXServer) {
140
- console.error('Failed to start kernel!');
77
+ this._pythonVersion = getPythonVersion(empackEnvMeta.packages);
78
+ this._prefix = empackEnvMeta.prefix;
79
+ const bootstrapped = await bootstrapEmpackPackedEnvironment({
80
+ empackEnvMeta,
81
+ pkgRootUrl: this._pkgRootUrl,
82
+ Module: this.Module,
83
+ logger: this.logger,
84
+ pythonVersion: this._pythonVersion
85
+ });
86
+ this._paths = bootstrapped.paths;
87
+ this._untarjs = bootstrapped.untarjs;
88
+ this._sharedLibs = bootstrapped.sharedLibs;
89
+ }
90
+ /**
91
+ * Initialize the interpreter if needed
92
+ * @param options
93
+ */
94
+ async initializeInterpreter(options) {
95
+ // Bootstrap Python, if it's xeus-python
96
+ if (options.kernelSpec.name === 'xpython') {
97
+ if (!this._pythonVersion) {
98
+ throw new Error('Python is not installed, cannot start Python!');
141
99
  }
142
- rawXKernel.start();
100
+ this.logger.log('Starting Python');
101
+ await bootstrapPython({
102
+ prefix: this._prefix,
103
+ pythonVersion: this._pythonVersion,
104
+ Module: this.Module
105
+ });
143
106
  }
144
- catch (e) {
145
- if (typeof e === 'number') {
146
- const msg = globalThis.Module.get_exception_message(e);
147
- throw new Error(msg);
107
+ // Load shared libs
108
+ await loadShareLibs({
109
+ sharedLibs: this._sharedLibs,
110
+ prefix: this._prefix,
111
+ Module: this.Module,
112
+ logger: this.logger
113
+ });
114
+ }
115
+ async install(channels, specs, pipSpecs) {
116
+ var _a, _b;
117
+ if (specs.length || pipSpecs.length) {
118
+ try {
119
+ this.logger.log(`Collecting ${(_a = (specs || pipSpecs)) === null || _a === void 0 ? void 0 : _a.join(',')}`);
120
+ const newPackages = await solve({
121
+ ymlOrSpecs: specs,
122
+ installedPackages: this._installedPackages,
123
+ pipSpecs,
124
+ channels,
125
+ logger: this.logger
126
+ });
127
+ await this._reloadPackagesInFS({
128
+ ...newPackages.condaPackages,
129
+ ...newPackages.pipPackages
130
+ });
148
131
  }
149
- else {
150
- throw e;
132
+ catch (error) {
133
+ (_b = this.logger) === null || _b === void 0 ? void 0 : _b.error(error.stack);
151
134
  }
152
135
  }
153
- kernelReady(1);
154
136
  }
155
137
  /**
156
- * Register the callback function to send messages from the worker back to the main thread.
157
- * @param callback the callback to register
138
+ * Implements dynamic installation of packages
158
139
  */
159
- registerCallback(callback) {
160
- this._sendWorkerMessage = callback;
140
+ listInstalledPackages() {
141
+ showPackagesList(this._installedPackages, this.logger);
142
+ return Promise.resolve();
143
+ }
144
+ async _reloadPackagesInFS(newInstalledPackages) {
145
+ const removedPackages = {};
146
+ const newPackages = {};
147
+ // First create structures we can quickly inspect
148
+ const newInstalledPackagesMap = {};
149
+ for (const newInstalledPkg of Object.values(newInstalledPackages)) {
150
+ newInstalledPackagesMap[newInstalledPkg.name] = newInstalledPkg;
151
+ }
152
+ const oldInstalledPackagesMap = {};
153
+ for (const oldInstalledPkg of Object.values(this._installedPackages)) {
154
+ oldInstalledPackagesMap[oldInstalledPkg.name] = oldInstalledPkg;
155
+ }
156
+ // Compare old installed packages with new ones
157
+ for (const filename of Object.keys(this._installedPackages)) {
158
+ const installedPkg = this._installedPackages[filename];
159
+ // Exact same build of the package already installed
160
+ if (installedPkg.name in newInstalledPackagesMap &&
161
+ installedPkg.build_string ===
162
+ newInstalledPackagesMap[installedPkg.name].build_string) {
163
+ continue;
164
+ }
165
+ removedPackages[filename] = installedPkg;
166
+ }
167
+ // Compare new installed packages with old ones
168
+ for (const filename of Object.keys(newInstalledPackages)) {
169
+ const newPkg = newInstalledPackages[filename];
170
+ // Exact same build of the package already installed
171
+ if (newPkg.name in oldInstalledPackagesMap &&
172
+ newPkg.build_string ===
173
+ oldInstalledPackagesMap[newPkg.name].build_string) {
174
+ continue;
175
+ }
176
+ newPackages[filename] = newPkg;
177
+ }
178
+ await removePackagesFromEmscriptenFS({
179
+ removedPackages,
180
+ Module: this.Module,
181
+ paths: this._paths,
182
+ logger: this.logger
183
+ });
184
+ const { sharedLibs, paths } = await installPackagesToEmscriptenFS({
185
+ packages: newPackages,
186
+ pkgRootUrl: this._pkgRootUrl,
187
+ pythonVersion: this._pythonVersion,
188
+ Module: this.Module,
189
+ untarjs: this._untarjs,
190
+ logger: this.logger
191
+ });
192
+ this._paths = { ...this._paths, ...paths };
193
+ await loadShareLibs({
194
+ sharedLibs,
195
+ prefix: this._prefix,
196
+ Module: this.Module,
197
+ logger: this.logger
198
+ });
199
+ this._installedPackages = newInstalledPackages;
161
200
  }
162
201
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyterlite/xeus",
3
- "version": "4.0.0-a0",
3
+ "version": "4.0.0-a10",
4
4
  "description": "JupyterLite Xeus kernels",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -36,19 +36,18 @@
36
36
  "watch:src": "tsc -w --sourceMap"
37
37
  },
38
38
  "dependencies": {
39
- "@emscripten-forge/mambajs": "^0.3.1",
40
- "@jupyterlab/coreutils": "^6",
41
- "@jupyterlab/services": "^7.3.4",
42
- "@jupyterlite/contents": "^0.2.0 || ^0.3.0 || ^0.4.5 || ^0.5.1",
43
- "@jupyterlite/kernel": "^0.2.0 || ^0.3.0 || ^0.4.5 || ^0.5.1",
44
- "@jupyterlite/server": "^0.2.0 || ^0.3.0 || ^0.4.5 || ^0.5.1",
39
+ "@emscripten-forge/mambajs": "^0.12.0",
40
+ "@jupyterlab/coreutils": "^6.4.2",
41
+ "@jupyterlab/services": "^7.4.2",
42
+ "@jupyterlite/contents": "^0.6.0-beta.0",
43
+ "@jupyterlite/kernel": "^0.6.0-beta.0",
44
+ "@jupyterlite/server": "^0.6.0-beta.0",
45
45
  "@lumino/coreutils": "^2",
46
46
  "@lumino/signaling": "^2",
47
47
  "coincident": "^1.2.3",
48
48
  "comlink": "^4.4.1"
49
49
  },
50
50
  "devDependencies": {
51
- "@jupyterlab/builder": "^4.1.0",
52
51
  "@types/json-schema": "^7.0.11",
53
52
  "@types/react": "^18.0.26",
54
53
  "@types/react-addons-linked-state-mixin": "^0.14.22",
@@ -56,7 +55,7 @@
56
55
  "rimraf": "^5.0.1",
57
56
  "source-map-loader": "^1.0.2",
58
57
  "ts-loader": "^9.2.6",
59
- "typescript": "~5.0.2",
58
+ "typescript": "^5.5",
60
59
  "webpack": "^5.87.0",
61
60
  "yjs": "^13.5.0"
62
61
  },