dragoncoder047’s blog

random thoughts about nonrandom things

Pyodide Issues

← Previous: Wireworld++ Next: Phoo is (mostly) finished
Posted
By dragoncoder047

I am currently working on an online console for Quackery that is using the Pyodide in-browser Python interpreter. The only bad thing is, Python’s input() function blocks until input is provided, which can’t be done in the browser because everything is asynchronous. The only way for it to work is an annoying prompt() box.

I thought, maybe I could automatically rewrite the Quackery source code and patch in an async input function, then make everything else that needs to be async, async.

The general process of patching went as follows:

  1. Find and replace the offending input() function with an async patched version.
  2. Find all the functions that are now invalid (i.e. are not declared with async def but now have an await inside of them) and make them async def.
  3. Find all the places where those functions are called, and await them.
  4. If something changed, go back to step 2.

My first attempt used the Python AST module to walk the abstract syntax tree and replace FunctionDef nodes with AsyncFunctionDef nodes and wrap their calls in Await nodes. For some reason, that either crapped out halfway through or hung forever. I don’t know why.

My second attempt was to use the regular expression module to find all the non-async functions that have await inside of them, and insert async onto the definition, then find their references and insert await. This proved tricky using regular expressions and often produced absurd (and incorrect) results such as async async def (until I checked and removed all occurrences of async async and await await manually) or even self.string_await from_stack() (because from_stack happened to be processed before string_from_stack and their names overlapped).

My third attempt was to simply manually go through and make the edits. The tricky thing here is, even though I am sure I have asynced and awaited everything I need to, Quackery operators (Python functions) are passed by reference to the interpreter, and some must be made async and some are not. And that wreaks havoc on the entire program, because it is impossible to predict whether that passed-by-reference function is async or not and whether it should be awaited. Whereas in Javascript – and Phoo, too – await undefined works, simply returning undefined immediately, await None throws TypeError: object NoneType can't be used in 'await' expression and crashes the whole program. So the TypeError must be suppressed with a try-catch block. Unfortunately, this causes some other problems, which I don’t really understand and so I can’t fix them.

I sure hope the Pyodide people can get the async input fixed soon!


Related Posts