This adds some basic ability to snapshot after executing user code. It is pretty
brittle right now:
1. It will crash if the user loads any binary extensions before taking the
snapshot
2. It doesn't track changes to the file system
Snapshots will probably have to be experimental for quite a while.
1. I think I have a pretty good solution for this, which I will work on in a
followup.
2. One possibility here is we could serialize the entire filesystem state into
the memory snapshot. This would be hard and make the snapshot big, but we
wouldn't have to load python_stdlib.zip when restoring from a snapshot so it
probably wouldn't increase the total download size by much...
Consider the code:
```js
pyodide.runPython(`
def a():
raise Exception("hi")
def b():
return 7;
`);
const a = pyodide.globals.get("a");
const b = pyodide.globals.get("b");
const p = a.callSyncifying();
assert(b() === 7);
await p;
```
This used to misbehave because because a()'s error status got stolen by b().
This happened because the promising function is a separate task from the js code
in callPyObjectSuspending, so the sequence of events goes:
- enter main task,
- enter callPyObjectSuspending(a)
- enter promisingApply(a)
- sets error flag and returns NULL
- queue continue callPyObjectSuspending(a) in event loop
now looks like [main task, continue callPyObjectSuspending(a)]
- enter b()
- enter Python
- returns 7 with error state still set
- rejects with "SystemError: <function b at 0x1140f20> returned a result with an exception set"
- queue continue main() in event loop
- continue callPyObjectSuspending(a)
- pythonexc2js called attempting to read error flag set by promisingApply(a), fails with
PythonError: TypeError: Pyodide internal error: no exception type or value
The solution: at the end of `_pyproxy_apply_promising` we move the error
flag into errStatus argument. In callPyObjectSuspending when we're ready we
move the error back from the errorStatus variable into the error flag before
calling `pythonexc2js()`
Buster isn't available for anymore for Python 312 so we have to update.
This also updates to Python 3.11.7, which turns out to be relatively invasive. I broadly
xfailed failing tests in `python_tests.yaml` since we'll upgrade to Python 3.12 soon anyways.
This change switches to my external implementation of hiwire. This is the
minimal change set to do this, it uses some hacks to avoid changing any files
outside of `hiwire.{c,h,js}`. In followups, I will gradually switch to using
the new APIs rather than compatibility shims.