Skip to content

Commit e07209c

Browse files
committed
Using Pyodide lockFileURL + packages when a cache exists
1 parent f8ffc38 commit e07209c

File tree

10 files changed

+135
-98
lines changed

10 files changed

+135
-98
lines changed

docs/index.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/index.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

esm/interpreter/pyodide.js

+40-8
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ import { create } from 'gc-hook';
33
import { RUNNING_IN_WORKER, createProgress, writeFile } from './_utils.js';
44
import { getFormat, loader, loadProgress, registerJSModule, run, runAsync, runEvent } from './_python.js';
55
import { stdio } from './_io.js';
6-
import { isArray } from '../utils.js';
6+
import { IDBMapSync, isArray } from '../utils.js';
77

88
const type = 'pyodide';
99
const toJsOptions = { dict_converter: Object.fromEntries };
1010

11+
const { stringify } = JSON;
12+
13+
// REQUIRES INTEGRATION TEST
1114
/* c8 ignore start */
1215
let overrideFunction = false;
1316
const overrideMethod = method => (...args) => {
@@ -75,10 +78,7 @@ const applyOverride = () => {
7578
};
7679

7780
const progress = createProgress('py');
78-
/* c8 ignore stop */
7981

80-
// REQUIRES INTEGRATION TEST
81-
/* c8 ignore start */
8282
export default {
8383
type,
8484
module: (version = '0.26.2') =>
@@ -88,15 +88,43 @@ export default {
8888
if (!RUNNING_IN_WORKER && config.experimental_create_proxy === 'auto')
8989
applyOverride();
9090
progress('Loading Pyodide');
91-
const { stderr, stdout, get } = stdio();
91+
let { packages } = config;
92+
progress('Loading Storage');
9293
const indexURL = url.slice(0, url.lastIndexOf('/'));
94+
// each pyodide version shares its own cache
95+
const storage = new IDBMapSync(indexURL);
96+
const options = { indexURL };
97+
const save = config.packages_cache !== 'never';
98+
await storage.sync();
99+
// packages_cache = 'never' means: erase the whole DB
100+
if (!save) storage.clear();
101+
// otherwise check if cache is known
102+
else if (packages) {
103+
// packages are uniquely stored as JSON key
104+
const key = stringify(packages);
105+
if (storage.has(key)) {
106+
const blob = new Blob(
107+
[storage.get(key)],
108+
{ type: 'application/json' },
109+
);
110+
// this should be used to bootstrap loadPyodide
111+
options.lockFileURL = URL.createObjectURL(blob);
112+
// no need to use micropip manually here
113+
options.packages = packages;
114+
packages = null;
115+
}
116+
}
117+
progress('Loaded Storage');
118+
const { stderr, stdout, get } = stdio();
93119
const interpreter = await get(
94-
loadPyodide({ stderr, stdout, indexURL }),
120+
loadPyodide({ stderr, stdout, ...options }),
95121
);
96122
const py_imports = importPackages.bind(interpreter);
97123
loader.set(interpreter, py_imports);
98124
await loadProgress(this, progress, interpreter, config, baseURL);
99-
if (config.packages) await py_imports(config.packages);
125+
// if cache wasn't know, import and freeze it for the next time
126+
if (packages) await py_imports(packages, storage, save);
127+
await storage.close();
100128
progress('Loaded Pyodide');
101129
return interpreter;
102130
},
@@ -130,7 +158,7 @@ function transform(value) {
130158
}
131159

132160
// exposed utility to import packages via polyscript.lazy_py_modules
133-
async function importPackages(packages) {
161+
async function importPackages(packages, storage, save = false) {
134162
// temporary patch/fix console.log which is used
135163
// not only by Pyodide but by micropip too and there's
136164
// no way to intercept those calls otherwise
@@ -146,6 +174,10 @@ async function importPackages(packages) {
146174
const micropip = this.pyimport('micropip');
147175
await micropip.install(packages, { keep_going: true });
148176
console.log = log;
177+
if (save && (storage instanceof IDBMapSync)) {
178+
const frozen = micropip.freeze();
179+
storage.set(stringify(packages), frozen);
180+
}
149181
micropip.destroy();
150182
}
151183
/* c8 ignore stop */

esm/loader.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ const parseString = config => {
4343
return toml(config);
4444
}
4545
};
46-
/* c8 ignore stop */
4746

4847
/**
4948
* Parse a generic config if it came from an attribute either as URL
@@ -58,10 +57,7 @@ const parseString = config => {
5857
*/
5958
export const getRuntime = (id, config, configURL, options = {}) => {
6059
if (config) {
61-
// REQUIRES INTEGRATION TEST
62-
/* c8 ignore start */
6360
[options, config] = resolveConfig(config, configURL, options);
64-
/* c8 ignore stop */
6561
}
6662
return resolve(options).then(options => interpreter[id](options, config));
6763
};
@@ -73,3 +69,5 @@ export const getRuntime = (id, config, configURL, options = {}) => {
7369
*/
7470
export const getRuntimeID = (type, version = '') =>
7571
`${type}@${version}`.replace(/@$/, '');
72+
73+
/* c8 ignore stop */

esm/script-handler.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { getRuntime, getRuntimeID } from './loader.js';
88
import { registry } from './interpreters.js';
99
import { JSModules, isSync, all, dispatch, resolve, defineProperty, nodeInfo, registerJSModules } from './utils.js';
1010

11+
/* c8 ignore start */
1112
const getRoot = (script) => {
1213
let parent = script;
1314
while (parent.parentNode) parent = parent.parentNode;
@@ -47,7 +48,6 @@ export const interpreters = new Map();
4748
const execute = async (currentScript, source, XWorker, isAsync) => {
4849
const { type } = currentScript;
4950
const module = registry.get(type);
50-
/* c8 ignore start */
5151
if (module.experimental)
5252
console.warn(`The ${type} interpreter is experimental`);
5353
const [interpreter, content] = await all([
@@ -77,7 +77,6 @@ const execute = async (currentScript, source, XWorker, isAsync) => {
7777
} finally {
7878
delete document.currentScript;
7979
}
80-
/* c8 ignore stop */
8180
};
8281

8382
const getValue = (ref, prefix) => {
@@ -95,10 +94,8 @@ export const getDetails = (type, id, name, version, config, configURL, runtime =
9594
interpreters.set(id, details);
9695
// enable sane defaults when single interpreter *of kind* is used in the page
9796
// this allows `xxx-*` attributes to refer to such interpreter without `env` around
98-
/* c8 ignore start *//* this is tested very well in PyScript */
9997
if (!interpreters.has(type)) interpreters.set(type, details);
10098
if (!interpreters.has(runtime)) interpreters.set(runtime, details);
101-
/* c8 ignore stopt */
10299
}
103100
return interpreters.get(id);
104101
};
@@ -163,7 +160,6 @@ export const handle = async (script) => {
163160
if (workerName) workers[workerName].resolve(xworker.ready);
164161
return;
165162
}
166-
/* c8 ignore stop */
167163

168164
const targetValue = getValue(target, '');
169165
const details = getDetails(type, id, name, versionValue, configValue);
@@ -182,3 +178,4 @@ export const handle = async (script) => {
182178
);
183179
}
184180
};
181+
/* c8 ignore stop */

esm/utils.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ const { all, resolve } = new Proxy(Promise, {
1717
get: ($, name) => $[name].bind($),
1818
});
1919

20+
/* c8 ignore start */
2021
const absoluteURL = (path, base = location.href) =>
2122
new URL(path, base.replace(/^blob:/, '')).href;
2223

23-
/* c8 ignore start */
2424
let id = 0;
2525
const nodeInfo = (node, type) => ({
2626
id: node.id || (node.id = `${type}-w${id++}`),

esm/worker/url.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { INVALID_CONTENT, INVALID_SRC_ATTR, INVALID_WORKER_ATTR } from '../error
22

33
import { dedent, unescape } from '../utils.js';
44

5+
/* c8 ignore start */ // tested via integration
56
const hasCommentsOnly = text => !text
67
.replace(/\/\*[\s\S]*?\*\//g, '')
78
.replace(/^\s*(?:\/\/|#).*/gm, '')
89
.trim()
910
;
1011

11-
/* c8 ignore start */ // tested via integration
1212
export default element => {
1313
const { src, worker } = element.attributes;
1414
if (worker) {

0 commit comments

Comments
 (0)