Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/wapython
Path: blob/main/python/python-wasm/src/node.ts
1067 views
1
import { syncKernel, asyncKernel, FileSystemSpec } from "@cowasm/kernel";
2
import { join } from "path";
3
import { existsSync } from "fs";
4
import debug from "debug";
5
6
const log = debug("python-wasm");
7
8
import { Options, PythonWasmSync, PythonWasmAsync } from "./common";
9
10
export type { Options, PythonWasmSync, PythonWasmAsync };
11
12
// This is used for build testing (all packages have a path).
13
export const path = __dirname;
14
15
const python_wasm = join(__dirname, "python.wasm");
16
const pythonEverything = join(__dirname, "python-everything.zip");
17
const pythonStdlib = join(__dirname, "python-stdlib.zip");
18
const pythonReadline = join(__dirname, "python-readline.zip");
19
const pythonMinimal = join(__dirname, "python-minimal.zip");
20
21
// For now this is the best we can do. TODO: cleanest solution in general would be to also include the
22
// python3.wasm binary (which has main) from the cpython package, to support running python from python.
23
// The following will only work in the build-from-source dev environment.
24
const PYTHONEXECUTABLE = join(__dirname, "../../cpython/bin/python-wasm");
25
26
export async function syncPython(
27
opts: Options = { fs: "everything" }
28
): Promise<PythonWasmSync> {
29
return (await createPython(true, opts)) as PythonWasmSync;
30
}
31
32
export async function asyncPython(
33
opts: Options = { fs: "everything" }
34
): Promise<PythonWasmAsync> {
35
return (await createPython(false, opts)) as PythonWasmAsync;
36
}
37
38
// also make this the default export for consistency with browser api.
39
export default asyncPython;
40
41
async function createPython(
42
sync: boolean,
43
opts: Options
44
): Promise<PythonWasmSync | PythonWasmAsync> {
45
opts = { fs: "everything", ...opts }; // default fs is everything
46
log("creating Python; sync = ", sync, ", opts = ", opts);
47
const fs = getFilesystem(opts);
48
let env: any = { PYTHONEXECUTABLE };
49
let wasm = python_wasm;
50
if (opts?.fs == "everything") {
51
wasm = "/usr/lib/python3.11/python.wasm";
52
}
53
if (opts?.fs == "everything") {
54
env.PYTHONHOME = "/usr";
55
}
56
if (opts?.env != null) {
57
env = { ...env, ...opts.env };
58
}
59
const kernel = sync
60
? await syncKernel({ env, fs })
61
: await asyncKernel({
62
env,
63
fs,
64
interactive: opts?.interactive,
65
noStdio: opts?.noStdio,
66
});
67
log("done");
68
log("initializing python");
69
const python = sync
70
? new PythonWasmSync(kernel as any, wasm)
71
: new PythonWasmAsync(kernel as any, wasm);
72
await python.init();
73
log("done");
74
return python;
75
}
76
77
function getFilesystem(opts?: Options): FileSystemSpec[] {
78
if (opts?.fs == "everything") {
79
return [
80
{
81
type: "zipfile",
82
zipfile: pythonEverything,
83
mountpoint: "/usr/lib/python3.11",
84
},
85
{ type: "native" },
86
];
87
}
88
if (opts?.fs == "stdlib") {
89
return [
90
{
91
type: "zipfile",
92
zipfile: pythonStdlib,
93
mountpoint: "/usr/lib/python3.11",
94
},
95
{ type: "native" },
96
];
97
}
98
if (opts?.fs == "bundle" || !existsSync(PYTHONEXECUTABLE)) {
99
// explicitly requested or not dev environment.
100
return [
101
// This will result in synchronously loading a tiny filesystem needed for starting python interpreter.
102
{
103
type: "zipfile",
104
zipfile: opts?.noReadline ? pythonMinimal : pythonReadline,
105
mountpoint: "/usr/lib/python3.11",
106
},
107
// Load full stdlib python filesystem asynchronously. Only needed to run actual interesting code.
108
// This way can load the wasm file from disk at the same time as the stdlib.
109
{
110
type: "zipfile",
111
async: true,
112
zipfile: pythonStdlib,
113
mountpoint: "/usr/lib/python3.11",
114
},
115
// And the rest of the native filesystem. **Sandboxing is not at all our goal here yet.**
116
{ type: "native" },
117
];
118
} else {
119
// native
120
return [{ type: "native" }];
121
}
122
}
123
124