Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support loadPyodide options #103

Merged
merged 7 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions examples/jupyter-lite.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
"jupyter-lite-schema-version": 0,
"jupyter-config-data": {
"appName": "JupyterLite Pyodide Kernel",
"disabledExtensions": {
"@jupyterlite/javascript-kernel-extension": true
"litePluginSettings": {
"@jupyterlite/pyodide-kernel-extension:kernel": {
"loadPyodideOptions": {
"packages": ["matplotlib", "micropip", "numpy", "sqlite3", "ssl"]
}
}
}
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jupyterlite/pyodide-kernel-root",
"version": "0.2.0",
"version": "0.3.0",
"private": true,
"workspaces": {
"packages": [
Expand Down Expand Up @@ -33,7 +33,7 @@
"lint:py": "jlpm lint:py:ruff:fix",
"lint:py:check": "jlpm lint:py:pip && jlpm lint:py:ruff:check",
"lint:py:pip": "python -m pip check",
"lint:py:ruff:fix": "ruff format && ruff --fix-only",
"lint:py:ruff:fix": "ruff format && ruff check --fix-only",
"lint:py:ruff:check": "ruff format --check",
"prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml,.yaml}\"",
"prettier:check": "jlpm prettier:base --check",
Expand Down
13 changes: 13 additions & 0 deletions packages/pyodide-kernel-extension/schema/kernel.v0.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@
},
"default": [],
"format": "uri"
},
"loadPyodideOptions": {
"type": "object",
"description": "additional options to provide to `loadPyodide`, see https://pyodide.org/en/stable/usage/api/js-api.html#globalThis.loadPyodide",
"default": {},
"properties": {
"packages": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
9 changes: 9 additions & 0 deletions packages/pyodide-kernel-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,21 @@ const kernel: JupyterLiteServerPlugin<void> = {
const config =
JSON.parse(PageConfig.getOption('litePluginSettings') || '{}')[PLUGIN_ID] || {};
const url = config.pyodideUrl || PYODIDE_CDN_URL;

const pyodideUrl = URLExt.parse(url).href;
const pipliteWheelUrl = config.pipliteWheelUrl
? URLExt.parse(config.pipliteWheelUrl).href
: undefined;
const rawPipUrls = config.pipliteUrls || [];
const pipliteUrls = rawPipUrls.map((pipUrl: string) => URLExt.parse(pipUrl).href);
const disablePyPIFallback = !!config.disablePyPIFallback;
const loadPyodideOptions = config.loadPyodideOptions || {};

for (const [key, value] of Object.entries(loadPyodideOptions)) {
if (key.endsWith('URL') && typeof value === 'string') {
loadPyodideOptions[key] = URLExt.parse(key).href;
}
}

kernelspecs.register({
spec: {
Expand Down Expand Up @@ -81,6 +89,7 @@ const kernel: JupyterLiteServerPlugin<void> = {
pipliteUrls,
disablePyPIFallback,
mountDrive,
loadPyodideOptions,
});
},
});
Expand Down
11 changes: 11 additions & 0 deletions packages/pyodide-kernel/src/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export class PyodideKernel extends BaseKernel implements IKernel {
const { pyodideUrl } = options;
const indexUrl = pyodideUrl.slice(0, pyodideUrl.lastIndexOf('/') + 1);
const baseUrl = PageConfig.getBaseUrl();

const pipliteUrls = [...(options.pipliteUrls || []), allJSONUrl.default];

const disablePyPIFallback = !!options.disablePyPIFallback;
Expand All @@ -67,6 +68,7 @@ export class PyodideKernel extends BaseKernel implements IKernel {
disablePyPIFallback,
location: this.location,
mountDrive: options.mountDrive,
loadPyodideOptions: options.loadPyodideOptions || {},
};
}

Expand Down Expand Up @@ -323,5 +325,14 @@ export namespace PyodideKernel {
* Whether or not to mount the Emscripten drive
*/
mountDrive: boolean;

/**
* additional options to provide to `loadPyodide`
* @see https://pyodide.org/en/stable/usage/api/js-api.html#globalThis.loadPyodide
*/
loadPyodideOptions: Record<string, any> & {
pyodideLockURL: string;
packages: string[];
};
}
}
9 changes: 9 additions & 0 deletions packages/pyodide-kernel/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,14 @@ export namespace IPyodideWorkerKernel {
* Whether or not to mount the Emscripten drive
*/
mountDrive: boolean;

/**
* additional options to provide to `loadPyodide`
* @see https://pyodide.org/en/stable/usage/api/js-api.html#globalThis.loadPyodide
*/
loadPyodideOptions: Record<string, any> & {
pyodideLockURL: string;
packages: string[];
};
}
}
57 changes: 38 additions & 19 deletions packages/pyodide-kernel/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export class PyodideRemoteKernel {
importScripts(pyodideUrl);
loadPyodide = (self as any).loadPyodide;
}
this._pyodide = await loadPyodide({ indexURL: indexUrl });
this._pyodide = await loadPyodide({
indexURL: indexUrl,
...options.loadPyodideOptions,
});
}

protected async initPackageManager(
Expand All @@ -60,38 +63,54 @@ export class PyodideRemoteKernel {
throw new Error('Uninitialized');
}

const { pipliteWheelUrl, disablePyPIFallback, pipliteUrls } = this._options;
const { pipliteWheelUrl, disablePyPIFallback, pipliteUrls, loadPyodideOptions } =
this._options;

await this._pyodide.loadPackage(['micropip']);
const preloaded = (loadPyodideOptions || {}).packages || [];

// get piplite early enough to impact pyodide dependencies
await this._pyodide.runPythonAsync(`
if (!preloaded.includes('micropip')) {
await this._pyodide.loadPackage(['micropip']);
}

if (!preloaded.includes('piplite')) {
await this._pyodide.runPythonAsync(`
import micropip
await micropip.install('${pipliteWheelUrl}', keep_going=True)
`);
}

// get piplite early enough to impact pyodide-kernel dependencies
await this._pyodide.runPythonAsync(`
import piplite.piplite
piplite.piplite._PIPLITE_DISABLE_PYPI = ${disablePyPIFallback ? 'True' : 'False'}
piplite.piplite._PIPLITE_URLS = ${JSON.stringify(pipliteUrls)}
`);
}

protected async initKernel(options: IPyodideWorkerKernel.IOptions): Promise<void> {
// from this point forward, only use piplite (but not %pip)
await this._pyodide.runPythonAsync(`
await piplite.install(['ssl'], keep_going=True);
await piplite.install(['sqlite3'], keep_going=True);
await piplite.install(['ipykernel'], keep_going=True);
await piplite.install(['comm'], keep_going=True);
await piplite.install(['pyodide_kernel'], keep_going=True);
await piplite.install(['ipython'], keep_going=True);
import pyodide_kernel
`);
const preloaded = (options.loadPyodideOptions || {}).packages || [];

const toLoad = ['ssl', 'sqlite3', 'ipykernel', 'comm', 'pyodide_kernel', 'ipython'];

const scriptLines: string[] = [];

// use piplite for packages that weren't pre-loaded
for (const pkgName of toLoad) {
if (!preloaded.includes(pkgName)) {
scriptLines.push(`await piplite.install('${pkgName}', keep_going=True)`);
}
}

// import the kernel
scriptLines.push('import pyodide_kernel');

// cd to the kernel location
if (options.mountDrive && this._localPath) {
await this._pyodide.runPythonAsync(`
import os;
os.chdir("${this._localPath}");
`);
scriptLines.push('import os', `os.chdir("${this._localPath}")`);
}

// from this point forward, only use piplite (but not %pip)
await this._pyodide.runPythonAsync(scriptLines.join('\n'));
}

protected async initGlobals(options: IPyodideWorkerKernel.IOptions): Promise<void> {
Expand Down
Loading