diff --git a/scripts/prepare-pyodide.js b/scripts/prepare-pyodide.js index 577b97854f..8ec57426cb 100644 --- a/scripts/prepare-pyodide.js +++ b/scripts/prepare-pyodide.js @@ -11,7 +11,6 @@ const packages = [ 'regex', 'sympy', 'tiktoken', - 'seaborn', 'pytz', 'black', 'openai' @@ -22,9 +21,21 @@ const packages = [ // Packages already provided by the Pyodide distribution (click, platformdirs, // typing_extensions, etc.) do NOT need to be listed here. // Spell them canonically (dashed): that is the only form pyodide resolves lock entries by. -const pypiPackages = ['black', 'pathspec', 'mypy-extensions', 'pytokens', 'openpyxl', 'et-xmlfile']; +const pypiPackages = [ + 'black', + 'pathspec', + 'mypy-extensions', + 'pytokens', + 'openpyxl', + 'et-xmlfile', + 'seaborn' +]; -const pypiDepends = { openpyxl: ['et-xmlfile'] }; +const pypiDepends = { + black: ['click', 'mypy-extensions', 'packaging', 'pathspec', 'platformdirs', 'pytokens'], + openpyxl: ['et-xmlfile'], + seaborn: ['matplotlib', 'numpy', 'pandas'] +}; import { loadPyodide } from 'pyodide'; import { setGlobalDispatcher, ProxyAgent } from 'undici'; @@ -196,6 +207,31 @@ async function downloadPyPIWheels() { console.log('Updated pyodide-lock.json with PyPI packages'); } +// A package with no bundled wheel is installed from PyPI in the user's browser instead. +async function verifyBundledWheels() { + const lockPath = 'static/pyodide/pyodide-lock.json'; + const lockData = JSON.parse(await readFile(lockPath, 'utf-8')); + const missing = []; + + for (const pkg of new Set([...packages, ...pypiPackages, ...Object.values(pypiDepends).flat()])) { + const entry = lockData.packages[pkg.toLowerCase().replace(/[-_.]+/g, '-')]; + if (!entry) { + missing.push(pkg); + continue; + } + try { + await access(`static/pyodide/${entry.file_name}`); + } catch { + missing.push(pkg); + } + } + + if (missing.length) { + throw new Error(`No wheel bundled for: ${missing.join(', ')}`); + } + console.log('All listed packages are bundled'); +} + initNetworkProxyFromEnv(); if (process.env.USE_SLIM === 'true') { // Rebuild generated assets so a previous full build cannot leave bundled wheels behind. @@ -217,4 +253,5 @@ if (process.env.USE_SLIM === 'true') { await downloadPackages(); await copyPyodide(); await downloadPyPIWheels(); + await verifyBundledWheels(); } diff --git a/src/lib/components/common/CodeEditor.svelte b/src/lib/components/common/CodeEditor.svelte index 64073e3a45..a9078d8b21 100644 --- a/src/lib/components/common/CodeEditor.svelte +++ b/src/lib/components/common/CodeEditor.svelte @@ -122,15 +122,7 @@ print(black.format_str("""${code.replace(/\\/g, '\\\\').replace(/`/g, '\\`').rep print("${endTag}") `; - // black's pyodide-lock entry declares no dependencies, so micropip installs none of them - const packages = [ - 'black', - 'click', - 'mypy_extensions', - 'pathspec', - 'platformdirs', - 'pytokens' - ]; + const packages = ['black']; function handleMessage(event) { const { id: eventId, stdout, stderr } = event.data; diff --git a/static/pyodide/pyodide-lock.json b/static/pyodide/pyodide-lock.json index 192f49b42b..25dc86aa67 100644 --- a/static/pyodide/pyodide-lock.json +++ b/static/pyodide/pyodide-lock.json @@ -5117,7 +5117,14 @@ "imports": [ "black" ], - "depends": [] + "depends": [ + "click", + "mypy-extensions", + "packaging", + "pathspec", + "platformdirs", + "pytokens" + ] }, "pathspec": { "name": "pathspec", @@ -5180,6 +5187,22 @@ "et_xmlfile" ], "depends": [] + }, + "seaborn": { + "name": "seaborn", + "version": "0.13.2", + "file_name": "seaborn-0.13.2-py3-none-any.whl", + "install_dir": "site", + "sha256": "636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", + "package_type": "package", + "imports": [ + "seaborn" + ], + "depends": [ + "matplotlib", + "numpy", + "pandas" + ] } } } \ No newline at end of file