Integrating PyGame with Skulpt but getting error when initialising pygame (pygame.init())
swickins opened this issue · comments
Trying to integrate pygame with skulpt using the code from here: https://github.com/Petlja/pygame4skulpt
The modules appear to import OK but when the code is executed, I am getting a
SuspensionError: Cannot call a function that blocks or suspends here on line 5
error, where pygame.init()
is on line 5.
Here is my code:
function runit() {
var prog = myCodeMirror.getValue();
var mypre = document.getElementById("output");
Sk.main_canvas = document.getElementById("myPgCanvas");
mypre.innerHTML = '';
Sk.pre = "output";
var externalLibs = {
"./pygame/__init__.js": "$pluginRootPath/js/pygame/__init__.js",
"./pygame/display.js": "$pluginRootPath/js/pygame/display.js",
"./pygame/draw.js": "$pluginRootPath/js/pygame/draw.js",
"./pygame/event.js": "$pluginRootPath/js/pygame/event.js",
"./pygame/font.js": "$pluginRootPath/js/pygame/font.js",
"./pygame/image.js": "$pluginRootPath/js/pygame/image.js",
"./pygame/key.js": "$pluginRootPath/js/pygame/key.js",
"./pygame/mouse.js": "$pluginRootPath/js/pygame/mouse.js",
"./pygame/time.js": "$pluginRootPath/js/pygame/time.js",
"./pygame/transform.js": "$pluginRootPath/js/pygame/transform.js",
"./pygame/version.js": "$pluginRootPath/js/pygame/version.js"
};
function builtinRead(file) {
console.log("Attempting file: " + Sk.ffi.remapToJs(file));
if (externalLibs[file] !== undefined) {
return Sk.misceval.promiseToSuspension(
fetch(externalLibs[file]).then(
function (resp){ return resp.text(); }
));
}
if (Sk.builtinFiles === undefined || Sk.builtinFiles.files[file] === undefined) {
throw "File not found: '" + file + "'";
}
return Sk.builtinFiles.files[file];
}
Sk.configure({
output:outf,
read:builtinRead,
});
//Sk.insertEvent("up");
//Sk.insertEvent("down");
//Sk.insertEvent("left");
//Sk.insertEvent("right");
var myPromise = Sk.misceval.asyncToPromise(function() {
return Sk.importMainWithBody("<stdin>", false, prog, true);
});
myPromise.then(function(mod) {
console.log('success');
},
function(err) {
console.log(err.toString());
outf(err.toString());
});
}
Can any skulpt experts help me please?
Your code looks good to me.
I think the problem could be in the implementation of pygame.
If you look at the source code here:
https://github.com/Petlja/pygame4skulpt/blob/a87cbe8dfb20e97d4e0b98c5692eaa33cc7c4da2/pygame/__init__.js#L1078-L1104
You can see that the call to pygame_init
does not support suspensions
But your read function is trying to asyncronously fetch the modules that pygame is trying to use - which will create a suspension.
Consider fetching all the pygame modules in one fetch so that the pygame_init
function doesn't need to suspend.
Thank you so much for your speedy response and help.
This makes sense but to be honest I have reused code posted by @albertjan so that I could import modules into the 'new' version of Skulpt and so therefore I am not entirely sure how to fetch all libraries in one go.
Would there be any chance that you could supply a code example to help?
Thanks again!
Untested but maybe something like this will work
let fetchedPygame = false;
function builtinRead(file) {
if (externalLibs[file] !== undefined) {
if (fetchedPygame) {
return externalLibs[file];
}
const promises = [];
for (const [file, url] of Object.entries(externalLibs)) {
promises.push(
fetch(url).then((resp) => {
externalLibs[file] = resp.text();
})
);
}
return Sk.misceval.promiseToSuspension(Promise.all(promises).then(() => {
fetchedPygame = true;
return externalLibs[file];
}));
}
// then the rest of the code
}
Firstly, thank you so much for your time and help.
I have just implemented the code but unfortunately it is now reverting back a simple 'ImportError: No module named pygame on line 4' error (with line 4 obviously being the import module statement.
There are no console errors but it just doesn't seem to import the module(s).
Not sure sorry - like I said - untested code - you'll probably need to add some console logging and debugger statements.
I'd start by logging the externalLibs
inside promiseToSuspension
.
Maybe wrap the code snippet in a try catch and log the error yourself.
I wouldn't be surprised if, when the builtinRead
throws an error, then skulpt throws an import error.
(I've already updated a couple of typos since I posted it...)
maybe also add some .catch(e => console.error(e))
to some of the promises.
Thank you once again.
Your code appears to work great in importing the module as I have no 'No Module' error message nor any Suspension Errors. Thanks so much!
However, I now get an ExternalError: SyntaxError: Unexpected identifier on line 4
where line 4 has pygame.init()
. :(
Thanks again for all your help. I will investigate why this error is happening but currently not sure as the modules seem to import so not sure why pygame won't initialise. If you have any ideas I would be so grateful but understand that I have already taken a lot of your time.
I would also add logging in this line
if (fetchedPygame) {
console.log(`loading ${file} source code from fetched files`);
return externalLibs[file];
}
an external error is an error from javascript.
So this seems like some sort of error in the source code of pygame (maybe)
OK, thank you again.
Will take a look
The console log always shows display.js
as if that is where the issue might be. But it the display.js file is taken straight from GitHub pygame4skulpt files. Unless I haven't set up the canvas properly. Hmmm
I think I have declared the canvas OK using Sk.main_canvas = document.getElementById("myPgCanvas");
and having a canvas
with ID myPgCanvas
in the HTML. So confused. :(
It's as if there is some inability to initialise the modules once they have been imported.
Here are my external libraries:
externalLibs = {
"./pygame/__init__.js": "$pluginRootPath/js/pygame/__init__.js",
"./pygame/display.js": "$pluginRootPath/js/pygame/display.js",
"./pygame/draw.js": "$pluginRootPath/js/pygame/draw.js",
"./pygame/event.js": "$pluginRootPath/js/pygame/event.js",
"./pygame/font.js": "$pluginRootPath/js/pygame/font.js",
"./pygame/image.js": "$pluginRootPath/js/pygame/image.js",
"./pygame/key.js": "$pluginRootPath/js/pygame/key.js",
"./pygame/mouse.js": "$pluginRootPath/js/pygame/mouse.js",
"./pygame/time.js": "$pluginRootPath/js/pygame/time.js",
"./pygame/transform.js": "$pluginRootPath/js/pygame/transform.js",
"./pygame/version.js": "$pluginRootPath/js/pygame/version.js"
};
and this is where the modules get initialise in __init__.js
:
// pygame module
function pygame_init() {
// ovo je mi ne izgleda najelegantnije, ali još nisam našao lepši način
var display_m = Sk.importModule("pygame.display", false, false);
var event_m = Sk.importModule("pygame.event", false, false);
var draw_m = Sk.importModule("pygame.draw", false, false);
var pygame_m = Sk.importModule("pygame", false, false);
var time_m = Sk.importModule("pygame.time", false, false);
var image_m = Sk.importModule("pygame.image", false, false);
var font_m = Sk.importModule("pygame.font", false, false);
var key_m = Sk.importModule("pygame.key", false, false);
var version_m = Sk.importModule("pygame.version", false, false);
var mouse_m = Sk.importModule("pygame.mouse", false, false);
var transform_m = Sk.importModule("pygame.transform", false, false);
PygameLib.initial_time = new Date();
pygame_m.$d['display'] = display_m.$d['display'];
pygame_m.$d['event'] = display_m.$d['event'];
pygame_m.$d['draw'] = display_m.$d['draw'];
pygame_m.$d['image'] = display_m.$d['image'];
delete PygameLib.eventQueue;
delete PygameLib.eventTimer;
PygameLib.eventQueue = [];
PygameLib.pressedKeys = {}
PygameLib.eventTimer = {};
PygameLib.running = true;
PygameLib.repeatKeys = false;
PygameLib.mouseData = { "button": [0, 0, 0], "pos": [0, 0], "rel": [0, 0] };
}
...and I notice that display.js is the first to be initialised.
Any ideas?
Oops my fault in the code snippet:
for (const [file, url] of Object.entries(externalLibs)) {
promises.push(
fetch(url).then((resp) => resp.text()).then((source) =>
externalLibs[file] = source;
})
);
}
I logged externalLibs and they were set to a bunch of Promises.
A note that I don't think pygame works with the latest version of skulpt.
@Dustyposa seems to have made a pr to try to fix that.
Thanks so much for your help. You are a star!
Oops my fault in the code snippet:
for (const [file, url] of Object.entries(externalLibs)) { promises.push( fetch(url).then((resp) => resp.text()).then((source) => externalLibs[file] = source; }) ); }I logged externalLibs and they were set to a bunch of Promises.
A note that I don't think pygame works with the latest version of skulpt. @Dustyposa seems to have made a pr to try to fix that.
Yes. @swickins
You can use I made the pr time to find the Skulpt at that time. Should be work.