skulpt / skulpt

Skulpt is a Javascript implementation of the Python programming language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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?

commented

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!

commented

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).

commented

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.

commented

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?

commented

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.