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
Run in node.js #14
Comments
Hi @mdboom , i made an attempt to load the code looks like this const path = require("path");
const wa = require("webassembly");
const wasmPath = path.join(__dirname, '/wasm/pyodide.asm.wasm');
const memory = new WebAssembly.Memory({ initial: 8192, maximum: 8192 });
const table = new WebAssembly.Table({ initial: 16384, element: "anyfunc" });
const importObj = { memory: memory, table: table };
wa.load(wasmPath, { imports: importObj }).then((module) => {
console.log(module.exports);
}).catch((err) => {
console.log(`some error: ${err}`);
}); i'm getting this error
can't find a solution for this, not sure what's causing it, maybe you have some idea? |
I wouldn't expect this one to be easy, but it would be really cool if we can find a solution ;) There are a bunch of things beyond just the Memory and Table that need to be set up to import the WebAssembly. In general, I wouldn't expect to be able to run emscripten-generated code directly like this. These variables are set up in In general, I would look at the code in Even then, I think there are probably some browser-specific things that emscripten is generating here that may break things in node. You may have to recompile pyodide with different settings. I would look at the emscripten docs for info on porting wasm from browser to node in general. |
Would you have the const importObj = {
global: {
NaN: NaN,
Infinity: Infinity
},
env: {
memory: memory,
table: table,
tableBase: 0,
memoryBase: 0,
DYNAMICTOP_PTR: 2042576,
tempDoublePtr: 2041040,
STACKTOP: 2042592,
STACK_MAX: 7285472,
gb: 1024,
fb: 0,
abort: function(cb) {},
enlargeMemory: function(){},
getTotalMemory: function(){}
}
}; i could just copy from a pretty Edit: don't mind, i managed to prettify here |
did you write |
|
hmm ok |
i'm almost there const path = require('path');
const fs = require('fs');
const wa = require('webassembly');
const pyodidejs = require('./src/wasm/pyodide.asm.js');
const wasmPath = path.join(__dirname, '/src/wasm/pyodide.asm.wasm');
const baseURL = path.join(__dirname, '/src/wasm/');
let pyodide;
function loadLanguage() {
return new Promise((resolve, reject) => {
let Module = {};
fetch_node(wasmPath).then((buffer) => buffer.arrayBuffer()).then((arrayBuffer) => {
let wasm_promise = WebAssembly.compile(arrayBuffer);
Module.instantiateWasm = (info, receiveInstance) => {
wasm_promise
.then(module => WebAssembly.instantiate(module, info))
.then(instance => receiveInstance(instance));
return {};
};
Module.filePackagePrefixURL = baseURL;
Module.postRun = () => {
Module = null;
console.log('will resolve'); // never gets here
resolve();
};
pyodide = pyodidejs(Module);
})
});
}
async function init() {
await loadLanguage();
console.log(pyodide.runPython);
console.log('Python loaded');
}
function fetch_node(file) {
return new Promise((resolve, reject) =>
fs.readFile(file, (err, data) => err ? reject(err) : resolve({ arrayBuffer: () => data })));
}
init(); got this error
let me know if you have any ideas |
Great progress! That looks like it's not finding the Python standard library (all the |
Hey @mdboom , there must be an option to generate it's been a pretty long journey today but i believe i've overridden the maximum things i could to make some stuff work in |
This is how the code looks like so far https://github.com/gabrielfreire/neuralnet.js/blob/wasm-nodejs/test.js ...
|
Wooooooooooooooohhh i did it... i'll push to github and send you the link for the final code |
That's great news! It's really nice to have those details worked out. Are you interested in working that into a pull request against Pyodide? We would need to find a way to work these changes into the build system -- either by applying the right compiler flags to emscripten to generate the right things, or possibly contributing changes to emscripten itself to emit cross-browser-and-node-compatible code. |
Yes, i still need to figure out how to load packages, but i can write python in Maybe there is a flag on emscripten to support
when i have everything tidy up and by the way, have you seen my PR on |
Awesome! |
It's working, i managed to load The main class: https://github.com/gabrielfreire/neuralnet.js/blob/wasm-nodejs/PyodideNode/PyodideNode.js i test on: https://github.com/gabrielfreire/neuralnet.js/blob/wasm-nodejs/test.js That's awesome. Minor support for I thought we would need to download the whole python library but there is no need for that, i think it gets loaded in the webassembly memory, i'm still reading the docs to understand webassembly better, i'm very new to this technology but i think it's very impressive. |
Very nice work on this!
According to the emscripten docs, at least emcc should generate code that also runs on node by default, cf for instance, https://github.com/kripken/emscripten/blob/6cc4c664e4db2d0e4172c298d4ec88519bce1997/src/settings.js#L321-L328 As to
If I understand the docs correctly, in browser the var fs = require('fs'); which talks to the node FS directly, while IMO to be environment independent it should be done via emscripten. Looking at if (ENVIRONMENT_IS_NODE) {
scriptDirectory = __dirname + "/";
var nodeFS;
var nodePath;
[...] so some node specific code seems to be included. You mentioned it before, but could you give an example of code that should be adapted for node? I couldn't find anything that looks like |
Hi @rth , thank you. var Module = typeof Module !== "undefined" ? Module : typeof process.Module !== 'undefined' ? process.Module : {}; there is no problem with this is an example of code modification included in var fetch = require('isomorphic-fetch'); // <-- for external resources
var loadPackage = function(metadata) {
var PACKAGE_PATH;
var PACKAGE_NAME = "build/pyodide.asm.data";
var REMOTE_PACKAGE_BASE = "pyodide.asm.data";
if (typeof Module["locateFilePackage"] === "function" && !Module["locateFile"]) {
Module["locateFile"] = Module["locateFilePackage"];
err("warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)")
}
var REMOTE_PACKAGE_NAME = Module["locateFile"] ? Module["locateFile"](REMOTE_PACKAGE_BASE, "") : REMOTE_PACKAGE_BASE;
var REMOTE_PACKAGE_SIZE = metadata.remote_package_size;
var PACKAGE_UUID = metadata.package_uuid;
console.log(REMOTE_PACKAGE_NAME)
function fetchRemotePackage(packageName, packageSize, callback, errback) {
function fetch_node(file) { // <-- for local resources
var fs = require('fs');
return new Promise((resolve, reject) =>
fs.readFile(file, (err, data) => err ? reject(err) : resolve({ arrayBuffer: () => data })));
}
fetch(packageName).then((buffer) => buffer.buffer()).then((packageData) => {
if (!Module.dataFileDownloads) Module.dataFileDownloads = {};
Module.dataFileDownloads[packageName] = {
loaded: packageSize,
total: packageSize
}
var total = 0;
var loaded = 0;
var num = 0;
for (var download in Module.dataFileDownloads) {
var data = Module.dataFileDownloads[download];
total += data.total;
loaded += data.loaded;
num++
}
total = Math.ceil(total * Module.expectedDataFileDownloads / num);
console.log(`Downloaded ${packageName} data... (${total}/${total})`);
callback(packageData);
if (Module["setStatus"]) Module["setStatus"]("Downloading data... (" + total + "/" + total + ")");
}).catch((err) => {
console.error(`Something wrong happened ${err}`);
throw new Error(`Something wrong happened ${err}`);
});
}
.
.
.
. i had to replace some function processPackageData(arrayBuffer) {
if(!arrayBuffer) throw "No input to processPackageData";
Module.finishedDataFileDownloads++;
if(!arrayBuffer instanceof ArrayBuffer) arrayBuffer = arrayBuffer.buffer ? arrayBuffer.buffer : null; // <- this is better
if(!arrayBuffer) throw "bad input to processPackageData"; i believe this is all i had to by hand to make pyodide work on Node.js, of course it looks easy now but i spent hours and hours debugging those modules to figure out what had to be done hehe, i almost gave up =D Conclusioni believe that |
Thanks for your response @gabrielfreire ! The question is how we can integrate your valuable contributions into pyodide :) As far as I understand the code in
Once we have this working, we can propose these modifications upstream in emscripten (or possibly it might be useful to open an issue in emscripten to start with). There is a number of open issues about node js in emscripten github repo (cf e.g. emscripten-core/emscripten#5237). I think a first step might be to just use load packages from some remote locations same as it happens in the browser. I was also wondering how to test this. One possibility could be to create another Selenium instance alongside Chrome/Firefox here, that would interact with Node JS via the Selenium Webdriver API. This would allow to run all tests also in NodeJS without much effort. However, I'm not sure this can work in practice: one possibility I saw for Node+Webdriver API was webdriver.io but I'm not sure how to call it from the Selenium-Python package (cf its documentation). Unless testing in that way would defeat the purpose of running in Node.js? |
@gabrielfreire Would you be interested in opening a PR for it? (If you think this is the right approach to apply your changes automatically). |
I really like this solution, i believe it is better to have another
Well, this could be a good idea, but for any new package we would have to manually replace a
i wasn't familiar on how you guys wrote the tests, i took a quick look and it's all python using selenium, if you want to re-use those tests for node.js i'm not sure how you would call it from python, the first thing that comes to my mind is ZeroMQ or RabbitMQ but i believe it should be pretty easy to re-write in pure node.js using
Well, i was working with files generated by @mdboom so i don't have 'pyodide' or any Once i have everything setup i'll apply the changes and generate something to see if it works well, then i open a PR. |
i think you mean, AFTER the new |
I guess I was hoping we could arrive at a
Not as far as I'm aware, but you could adapt the |
Great!
It is strange indeed that it does some environment check for the file system but not for the these lines make sure you will have an error thrown if you're not in a browser var PACKAGE_PATH;
if (typeof window === 'object') {
PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/');
} else if (typeof location !== 'undefined') {
// worker
PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/');
} else {
throw 'using preloaded data can only be done on a web page, web worker or in nodejs';
} A possible change would be something like this var PACKAGE_PATH;
if (typeof window === 'object') {
PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/');
console.warn('Browser environment'); // BROWSER
} else if (typeof location !== 'undefined') {
// worker
PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/');
console.warn('Web worker environment'); // WEB WORKER
} else if (typeof process === "object" && typeof require === "function") {
console.warn('Node environment'); // NODE
} else {
throw 'using preloaded data can only be done on a web page, web worker or in nodejs';
} the above code is me already working on some changes for |
@rth , i'm getting a strange error with the new exception thrown: ReferenceError: window is not defined,ReferenceError: window is not defined
at _hiwire_get_global (C:\Users\gabriel.freire\Documents\workspace\neuralnet.js\PyodideNode\pyodide.asm.js:1552:36)
at wasm-function[312]:290
at wasm-function[10266]:10
at wasm-function[488]:348
at wasm-function[2631]:32923
at wasm-function[14870]:7
at wasm-function[2626]:3888
at wasm-function[2625]:31
at wasm-function[2587]:676
at wasm-function[11339]:7 It gets fixed when i change this function function _hiwire_get_global(idname) {
var jsname = UTF8ToString(idname);
return Module.hiwire_new_value(window[jsname])
} to function _hiwire_get_global(idname) {
var jsname = UTF8ToString(idname);
return Module.hiwire_new_value(jsname)
} but this change could brake something else so i don't think this is the problem because the old |
This is great work! I think it makes sense to have a local copy of I can help get that merged back upstream into emscripten if there are any delays. As for the references to As for testing, ideally we'd find a way to use the same tests in all contexts. We could try writing some tests in a Node-based test harness (rather than py.test) so we could run code in-process in Node itself, and then use |
What happened to this exciting development? Is running pyodide under nodejs possible/supported these days? |
See the latest status in #183 (comment) which is still up to date. |
#183 is now closed Nodejs support would be a huge plus for many electron/node apps depending on Python applications--examples include VSCode extensions that no longer need to bundle a whole Python installation and what not to enable features. Is there any more information about this? Glad to jump in to help if there is. |
The most recent discussion about related stuff was in #1477. The strict mode issues in #1477 have been dealt with and I think we exclusively use That said, I don't think any of the current contributors has much knowledge of or experience with any of these platforms (node, deno, electron), and there is a limited amount of developer energy. So basically we've been resolving issues that are known blockers for node integration, but we haven't been directly trying to get it working ourselves. An important step would be just to try to run Pyodide on your favored platform and open issues when you run into trouble. We are very open to including changes to increase the range of platforms supported. Once we have basic support, if we added at least some basic node tests to the CI it would upgrade node support from an afterthought to more of a first-class feature.
I can answer specific questions about how the Pyodide runtime works. I'm not sure what sort of information you need. Help is very welcome. |
With the last few changes in #1691 using Pyodide in Node.js with local files should work. I'm looking for input on what you would like to see in the Pyodide npm package (related discussion in #328 and #1403), particularly when running Pyodide in Node.js.
Feedback welcome. For now I'm considering going with option 3, unless there are objections. The other thing that's not clear to me, is how to combine in the same NPM package the use case a) of frontend app development and producing some .js files with rollup b) running Pyodide in Node. Say we need some arbitrary package for running in Node, and add it to the dependencies. Is this potentially an issue for use case a) or it wouldn't matter? cc @hoodmane @dmondev @teonbrooks @bollwyvl @jtpio @rebornix |
Yeah I would be interested to know the answer to this, my impression was that this is inconvenient but I have only ever used node for front end stuff. Here is one possible solution: |
For my highly-biased purposes (entirely informed by jupyterlite), I could care less whether
I am, however, interested in being able to run these in |
we've actually been working on migrating our Python requirement in our Electron app from native Python to Pyodide (makebrainwaves/BrainWaves#157). We've currently done this by downloading the entire versioned zip, and serve the files from a utils folder with the app (added to our webpack config). our biggest requirements have been that:
for our package dependencies, we don't need all the supported packages but we haven't had a need to prune it. cc: @jdpigeon can you think of anything else we would need from a node-compatible pyodide? |
Thanks for the feedback @bollwyvl @teonbrooks! It's helpful to have your input on these different use cases. We likely make an alpha release for the npm package in the near future, and it will be more clear then if there are any issues for your use cases. |
Hi, we've been investigating how to make Pyodide and VS Code work, and allowing Pyodide to run in Node.js would be very helpful since VS Code is based on Node. It would be great to have the the ability to use Pyodide with VS Code through Node support, not only for us but also for our users. Thanks! |
@eleanorjboyd just a heads up that VSCode is Electron (which bundles Node); this (I assume) should not cause any issues except if the final Pyodide build for NodeJS uses native NodeJS modules (which should not be the case, otherwise Pyodide shouldn't run on Web at all!) (VSCode-relevant discussion below) |
FWIW it is possible to use native node modules in VS Code but this gets troublesome for extensions if the platform specific binaries have to be built and bundled with the VS Code extension. If the native node modules in question use Node-API there's no issue.
As a first step, we can publish an extension which exposes the Pyodide instance via an API, and then other extensions can declare a dependency on that with |
@joyceerhl thanks for that--can we discuss further in a separate issue under vscode to reduce noise here? (VSCode-relevant discussion below)
I wonder if there was any discussion of making |
Okay I moved the vscode discussion over to #1712 (I don't think I can actually move peoples' comments so I just quoted them over). |
I am closing this as resolved. We have been running the full test suite in node as part of the CI for months now. Please feel free to open a new issue if your use case isn't supported. |
This is also being tested nightly against CPython |
I don't really understand the implications of this or whether it's possible, but it might be handy for testing.
The text was updated successfully, but these errors were encountered: