148 lines
4.0 KiB
JavaScript
148 lines
4.0 KiB
JavaScript
import { loadPyodide } from "./pyodide.mjs";
|
|
import { readdirSync } from "fs";
|
|
|
|
/**
|
|
* Determine which native top level directories to mount into the Emscripten
|
|
* file system.
|
|
*
|
|
* This is a bit brittle, if the machine has a top level directory with certain
|
|
* names it is possible this could break. The most surprising one here is tmp, I
|
|
* am not sure why but if we link tmp then the process silently fails.
|
|
*/
|
|
function dirsToMount() {
|
|
return readdirSync("/")
|
|
.filter((dir) => !["dev", "lib", "proc"].includes(dir))
|
|
.map((dir) => "/" + dir);
|
|
}
|
|
|
|
const thisProgramFlag = "--this-program=";
|
|
const thisProgramIndex = process.argv.findIndex((x) =>
|
|
x.startsWith(thisProgramFlag),
|
|
);
|
|
const args = process.argv.slice(thisProgramIndex + 1);
|
|
const _sysExecutable = process.argv[thisProgramIndex].slice(
|
|
thisProgramFlag.length,
|
|
);
|
|
|
|
function fsInit(FS) {
|
|
const mounts = dirsToMount();
|
|
for (const mount of mounts) {
|
|
FS.mkdirTree(mount);
|
|
FS.mount(FS.filesystems.NODEFS, { root: mount }, mount);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
let py;
|
|
try {
|
|
py = await loadPyodide({
|
|
args,
|
|
_sysExecutable,
|
|
env: Object.assign(
|
|
{
|
|
PYTHONINSPECT: "",
|
|
},
|
|
process.env,
|
|
{ HOME: process.cwd() },
|
|
),
|
|
fullStdLib: false,
|
|
fsInit,
|
|
});
|
|
} catch (e) {
|
|
if (e.constructor.name !== "ExitStatus") {
|
|
throw e;
|
|
}
|
|
// If the user passed `--help`, `--version`, or a set of command line
|
|
// arguments that is invalid in some way, we will exit here.
|
|
process.exit(e.status);
|
|
}
|
|
py.setStdout();
|
|
py.setStderr();
|
|
let sideGlobals = py.runPython("{}");
|
|
function handleExit(code) {
|
|
if (code === undefined) {
|
|
code = 0;
|
|
}
|
|
if (py._module._Py_FinalizeEx() < 0) {
|
|
code = 120;
|
|
}
|
|
// It's important to call `process.exit` immediately after
|
|
// `_Py_FinalizeEx` because otherwise any asynchronous tasks still
|
|
// scheduled will segfault.
|
|
process.exit(code);
|
|
}
|
|
sideGlobals.set("handleExit", handleExit);
|
|
|
|
py.runPython(
|
|
`
|
|
from pyodide._package_loader import SITE_PACKAGES, should_load_dynlib
|
|
from pyodide.ffi import to_js
|
|
import re
|
|
dynlibs_to_load = to_js([
|
|
str(path) for path in SITE_PACKAGES.glob("**/*.so*")
|
|
if should_load_dynlib(path)
|
|
])
|
|
`,
|
|
{ globals: sideGlobals },
|
|
);
|
|
const dynlibs = sideGlobals.get("dynlibs_to_load");
|
|
for (const dynlib of dynlibs) {
|
|
try {
|
|
await py._module.API.loadDynlib(dynlib);
|
|
} catch (e) {
|
|
console.error("Failed to load lib ", dynlib);
|
|
console.error(e);
|
|
}
|
|
}
|
|
py.runPython(
|
|
`
|
|
import asyncio
|
|
# Keep the event loop alive until all tasks are finished, or SystemExit or
|
|
# KeyboardInterupt is raised.
|
|
loop = asyncio.get_event_loop()
|
|
# Make sure we don't run _no_in_progress_handler before we finish _run_main.
|
|
loop._in_progress += 1
|
|
loop._no_in_progress_handler = handleExit
|
|
loop._system_exit_handler = handleExit
|
|
loop._keyboard_interrupt_handler = lambda: handleExit(130)
|
|
|
|
# Make shutil.get_terminal_size tell the terminal size accurately.
|
|
import shutil
|
|
from js.process import stdout
|
|
import os
|
|
def get_terminal_size(fallback=(80, 24)):
|
|
columns = getattr(stdout, "columns", None)
|
|
rows = getattr(stdout, "rows", None)
|
|
if columns is None:
|
|
columns = fallback[0]
|
|
if rows is None:
|
|
rows = fallback[1]
|
|
return os.terminal_size((columns, rows))
|
|
shutil.get_terminal_size = get_terminal_size
|
|
`,
|
|
{ globals: sideGlobals },
|
|
);
|
|
|
|
let errcode;
|
|
try {
|
|
if (py._module.jspiSupported) {
|
|
errcode = await py._module.promisingRunMain();
|
|
} else {
|
|
errcode = py._module._run_main();
|
|
}
|
|
} catch (e) {
|
|
if (e.constructor.name === "ExitStatus") {
|
|
process.exit(e.status);
|
|
}
|
|
py._api.fatal_error(e);
|
|
}
|
|
if (errcode) {
|
|
process.exit(errcode);
|
|
}
|
|
py.runPython("loop._decrement_in_progress()", { globals: sideGlobals });
|
|
}
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|