Download standard libraries in a zipfile. ### Description

This is a re-work of #3166. After this PR, the standard libraries are not vendored with `pyodide.asm.data`. Instead, it is now downloaded separately in a zip file similar to how we download pyodide_py.tar.

~~The hard part of this PR is to bootstrap Python without Python source codes. This requires deep-freezing all modules that are required during the bootstrap.~~

~~As mentioned in #3539, `encodings` module is needed during the bootstrap stage, but it is a large module (> 1.0MB) containing a bunch of encodings in the world, so I tried to freeze a subset of the encodings module that is used in the bootstrap (thanks for the idea from Hood), so the size increase of pyodide.asm.wasm is only 70KB.~~

~~However, the problem of freezing only part of the module is that the current Python import system does not support it. Therefore, I added some hacks to make it work, which is explained in a comment in pyodide.ts.~~

### File size change after this PR:

Before

```
- pyodide.asm.data:
    Original size: 4555 KB
    Gzip compressed size (level 1): 3062 KB
    Gzip compressed size (level 6): 2911 KB
    Gzip compressed size (level 9): 2911 KB
    Brotli compressed size: 2522 KB
- pyodide.asm.js:
    Original size: 1186 KB
    Gzip compressed size (level 1): 246 KB
    Gzip compressed size (level 6): 208 KB
    Gzip compressed size (level 9): 208 KB
    Brotli compressed size: 169 KB
- pyodide.asm.wasm:
    Original size: 8715 KB
    Gzip compressed size (level 1): 3142 KB
    Gzip compressed size (level 6): 2791 KB
    Gzip compressed size (level 9): 2791 KB
    Brotli compressed size: 2096 KB
```

After

```
- pyodide.asm.data:
    Original size: 4 KB
    Gzip compressed size (level 1): 0 KB
    Gzip compressed size (level 6): 0 KB
    Gzip compressed size (level 9): 0 KB
    Brotli compressed size: 0 KB
- pyodide.asm.js:
    Original size: 1086 KB
    Gzip compressed size (level 1): 213 KB
    Gzip compressed size (level 6): 177 KB
    Gzip compressed size (level 9): 177 KB
    Brotli compressed size: 145 KB
- pyodide.asm.wasm:
    Original size: 8775 KB
    Gzip compressed size (level 1): 3162 KB
    Gzip compressed size (level 6): 2806 KB
    Gzip compressed size (level 9): 2806 KB
    Brotli compressed size: 2105 KB
- python_stdlib.zip:
    Original size: 2230 KB
    Gzip compressed size (level 1): 2196 KB
    Gzip compressed size (level 6): 2194 KB
    Gzip compressed size (level 9): 2194 KB
    Brotli compressed size: 2181 KB
```


### Remaining works:

- remove pyodide.asm.data: the only remaining parts inside pyodide.asm.data is our [webbrowser.py](https://github.com/pyodide/pyodide/blob/main/src/py/lib/webbrowser.py) stub, I think we can either remove it or move it into pyodide_py.
- maybe merge pyodide_py.tar into the standard libraries zipfile.
- add option to use pycompiled standard libraries.

### Checklists

- [x] Add a [CHANGELOG](https://github.com/pyodide/pyodide/blob/main/docs/project/changelog.md) entry
- [x] Add / update tests

(interrupting_execution)=

# Interrupting execution

The native Python interrupt system is based on preemptive multitasking but Web
Assembly has no support for preemptive multitasking. Because of this,
interrupting execution in Pyodide must be achieved via a different mechanism
which takes some effort to set up.

## Setting up interrupts

In order to use interrupts you must be using Pyodide in a webworker.
You also will need to use a {js:class}`SharedArrayBuffer`, which means that your
server
must set appropriate security headers. See [the MDN
docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements)
for more information.

To use the interrupt system, you should create a {js:class}`SharedArrayBuffer`
on either the main thread or the worker thread and share it with the other
thread. You should use {js:func}`pyodide.setInterruptBuffer` to set the
interrupt buffer on the Pyodide thread. When you want to indicate an interrupt,
write a `2` into the interrupt buffer. When the interrupt signal is processed,
Pyodide will set the value of the interrupt buffer back to `0`.

By default, when the interrupt fires, a {py:exc}`KeyboardInterrupt` is raised.
Using the {py:mod}`signal` module, it is possible to register a custom Python
function to handle {py:data}`~signal.SIGINT`. If you register a custom handler
function it will be called instead.

Here is a very basic example. Main thread code:

```js
let pyodideWorker = new Worker("pyodideWorker.js");
let interruptBuffer = new Uint8Array(new SharedArrayBuffer(1));
pyodideWorker.postMessage({ cmd: "setInterruptBuffer", interruptBuffer });
function interruptExecution() {
  // 2 stands for SIGINT.
  interruptBuffer[0] = 2;
}
// imagine that interruptButton is a button we want to trigger an interrupt.
interruptButton.addEventListener("click", interruptExecution);
async function runCode(code) {
  // Clear interruptBuffer in case it was accidentally left set after previous code completed.
  interruptBuffer[0] = 0;
  pyodideWorker.postMessage({ cmd: "runCode", code });
}
```

Worker code:

```js
self.addEventListener("message", (msg) => {
  if (msg.data.cmd === "setInterruptBuffer") {
    pyodide.setInterruptBuffer(msg.data.interruptBuffer);
    return;
  }
  if (msg.data.cmd === "runCode") {
    pyodide.runPython(msg.data.code);
    return;
  }
});
```

## Allowing JavaScript code to be interrupted

The interrupt system above allows interruption of Python code and also of C code
that allows itself to be interrupted by periodically calling
{c:func}`PyErr_CheckSignals`. There is also a function
{js:func}`pyodide.checkInterrupt` that allows JavaScript functions called from
Python to check for an interrupt. As a simple example, we can implement an
interruptible sleep function using {js:func}`Atomics.wait`:

```js
let blockingSleepBuffer = new Int32Array(new SharedArrayBuffer(4));
function blockingSleep(t) {
  for (let i = 0; i < t * 20; i++) {
    // This Atomics.wait call blocks the thread until the buffer changes or a 50ms timeout elapses.
    // Since we won't change the value in the buffer, this blocks for 50ms.
    Atomics.wait(blockingSleepBuffer, 0, 0, 50);
    // Periodically check for an interrupt to allow a KeyboardInterrupt.
    pyodide.checkInterrupt();
  }
}
```

(interrupting_execution)=

# Interrupting execution

The native Python interrupt system is based on preemptive multitasking but Web
Assembly has no support for preemptive multitasking. Because of this,
interrupting execution in Pyodide must be achieved via a different mechanism
which takes some effort to set up.

## Setting up interrupts

In order to use interrupts you must be using Pyodide in a webworker.
You also will need to use a {js:class}`SharedArrayBuffer`, which means that your
server
must set appropriate security headers. See [the MDN
docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements)
for more information.

To use the interrupt system, you should create a {js:class}`SharedArrayBuffer`
on either the main thread or the worker thread and share it with the other
thread. You should use {js:func}`pyodide.setInterruptBuffer` to set the
interrupt buffer on the Pyodide thread. When you want to indicate an interrupt,
write a `2` into the interrupt buffer. When the interrupt signal is processed,
Pyodide will set the value of the interrupt buffer back to `0`.

By default, when the interrupt fires, a {py:exc}`KeyboardInterrupt` is raised.
Using the {py:mod}`signal` module, it is possible to register a custom Python
function to handle {py:data}`~signal.SIGINT`. If you register a custom handler
function it will be called instead.

Here is a very basic example. Main thread code:

```js
let pyodideWorker = new Worker("pyodideWorker.js");
let interruptBuffer = new Uint8Array(new SharedArrayBuffer(1));
pyodideWorker.postMessage({ cmd: "setInterruptBuffer", interruptBuffer });
function interruptExecution() {
  // 2 stands for SIGINT.
  interruptBuffer[0] = 2;
}
// imagine that interruptButton is a button we want to trigger an interrupt.
interruptButton.addEventListener("click", interruptExecution);
async function runCode(code) {
  // Clear interruptBuffer in case it was accidentally left set after previous code completed.
  interruptBuffer[0] = 0;
  pyodideWorker.postMessage({ cmd: "runCode", code });
}
```

Worker code:

```js
self.addEventListener("message", (msg) => {
  if (msg.data.cmd === "setInterruptBuffer") {
    pyodide.setInterruptBuffer(msg.data.interruptBuffer);
    return;
  }
  if (msg.data.cmd === "runCode") {
    pyodide.runPython(msg.data.code);
    return;
  }
});
```

## Allowing JavaScript code to be interrupted

The interrupt system above allows interruption of Python code and also of C code
that allows itself to be interrupted by periodically calling
{c:func}`PyErr_CheckSignals`. There is also a function
{js:func}`pyodide.checkInterrupt` that allows JavaScript functions called from
Python to check for an interrupt. As a simple example, we can implement an
interruptible sleep function using {js:func}`Atomics.wait`:

```js
let blockingSleepBuffer = new Int32Array(new SharedArrayBuffer(4));
function blockingSleep(t) {
  for (let i = 0; i < t * 20; i++) {
    // This Atomics.wait call blocks the thread until the buffer changes or a 50ms timeout elapses.
    // Since we won't change the value in the buffer, this blocks for 50ms.
    Atomics.wait(blockingSleepBuffer, 0, 0, 50);
    // Periodically check for an interrupt to allow a KeyboardInterrupt.
    pyodide.checkInterrupt();
  }
}
```
